Browse Source

Merge branch 'develop' into new_branch_24_04

Philippe Grand 8 years ago
parent
commit
b5b79bc36b
100 changed files with 3111 additions and 933 deletions
  1. 3 1
      ChangeLog
  2. 22 7
      build/debian/README.howto
  3. 1 1
      build/debian/install.forced.php.install
  4. 2 1
      build/makepack-dolibarr.pl
  5. 3 4
      htdocs/accountancy/admin/accountmodel.php
  6. 1 1
      htdocs/accountancy/admin/export.php
  7. 2 2
      htdocs/accountancy/admin/index.php
  8. 1 1
      htdocs/accountancy/admin/journals.php
  9. 6 2
      htdocs/accountancy/bookkeeping/card.php
  10. 2 2
      htdocs/accountancy/bookkeeping/list.php
  11. 26 22
      htdocs/accountancy/bookkeeping/listbyaccount.php
  12. 12 3
      htdocs/accountancy/class/bookkeeping.class.php
  13. 12 4
      htdocs/accountancy/customer/lines.php
  14. 9 4
      htdocs/accountancy/customer/list.php
  15. 6 3
      htdocs/accountancy/index.php
  16. 153 138
      htdocs/accountancy/journal/bankjournal.php
  17. 146 104
      htdocs/accountancy/journal/expensereportsjournal.php
  18. 153 106
      htdocs/accountancy/journal/purchasesjournal.php
  19. 139 97
      htdocs/accountancy/journal/sellsjournal.php
  20. 7 2
      htdocs/accountancy/supplier/lines.php
  21. 7 2
      htdocs/accountancy/supplier/list.php
  22. 3 3
      htdocs/adherents/subscription.php
  23. 1 1
      htdocs/adherents/subscription/card.php
  24. 1 1
      htdocs/admin/agenda.php
  25. 1 1
      htdocs/admin/agenda_extrafields.php
  26. 1 1
      htdocs/admin/agenda_extsites.php
  27. 1 1
      htdocs/admin/agenda_other.php
  28. 1 1
      htdocs/admin/agenda_xcal.php
  29. 7 1
      htdocs/admin/chequereceipts.php
  30. 2 1
      htdocs/admin/clicktodial.php
  31. 7 6
      htdocs/admin/commande.php
  32. 9 2
      htdocs/admin/contract.php
  33. 84 20
      htdocs/admin/defaultvalues.php
  34. 20 14
      htdocs/admin/dict.php
  35. 8 2
      htdocs/admin/expedition.php
  36. 9 3
      htdocs/admin/expensereport.php
  37. 1 1
      htdocs/admin/expensereport_extrafields.php
  38. 10 6
      htdocs/admin/external_rss.php
  39. 7 6
      htdocs/admin/facture.php
  40. 10 3
      htdocs/admin/fichinter.php
  41. 7 1
      htdocs/admin/livraison.php
  42. 1 3
      htdocs/admin/loan.php
  43. 19 12
      htdocs/admin/mails_templates.php
  44. 2 4
      htdocs/admin/modules.php
  45. 4 9
      htdocs/admin/oauth.php
  46. 1 1
      htdocs/admin/payment.php
  47. 42 3
      htdocs/admin/pdf.php
  48. 1 3
      htdocs/admin/perms.php
  49. 8 7
      htdocs/admin/propal.php
  50. 1 1
      htdocs/admin/salaries.php
  51. 56 0
      htdocs/admin/stock.php
  52. 9 4
      htdocs/admin/supplier_invoice.php
  53. 10 4
      htdocs/admin/supplier_order.php
  54. 1 2
      htdocs/admin/supplier_payment.php
  55. 9 2
      htdocs/admin/supplier_proposal.php
  56. 14 8
      htdocs/admin/syslog.php
  57. 5 5
      htdocs/admin/taxes.php
  58. 21 5
      htdocs/admin/translation.php
  59. 17 34
      htdocs/admin/websites.php
  60. 1 2
      htdocs/admin/workflow.php
  61. 2 0
      htdocs/api/class/api.class.php
  62. 1 1
      htdocs/api/class/api_login.class.php
  63. 8 4
      htdocs/comm/card.php
  64. 27 24
      htdocs/comm/mailing/cibles.php
  65. 1 1
      htdocs/comm/mailing/class/mailing.class.php
  66. 5 4
      htdocs/comm/propal/class/api_proposals.class.php
  67. 2 2
      htdocs/comm/propal/contact.php
  68. 1 0
      htdocs/commande/class/commande.class.php
  69. 3 2
      htdocs/commande/contact.php
  70. 1 1
      htdocs/compta/bank/card.php
  71. 1 1
      htdocs/compta/facture/admin/facture_cust_extrafields.php
  72. 1 1
      htdocs/compta/facture/admin/facturedet_cust_extrafields.php
  73. 11 22
      htdocs/compta/facture/card.php
  74. 2 1
      htdocs/compta/facture/fiche-rec.php
  75. 19 19
      htdocs/compta/paiement.php
  76. 55 5
      htdocs/compta/paiement/class/paiement.class.php
  77. 6 1
      htdocs/core/ajax/check_notifications.php
  78. 5 2
      htdocs/core/boxes/box_external_rss.php
  79. 2 1
      htdocs/core/class/commoninvoice.class.php
  80. 367 56
      htdocs/core/class/commonobject.class.php
  81. 3 3
      htdocs/core/class/conf.class.php
  82. 440 0
      htdocs/core/class/coreobject.class.php
  83. 500 0
      htdocs/core/class/ctyperesource.class.php
  84. 4 0
      htdocs/core/class/extrafields.class.php
  85. 4 3
      htdocs/core/class/html.form.class.php
  86. 14 5
      htdocs/core/class/html.formmail.class.php
  87. 7 7
      htdocs/core/class/html.formmailing.class.php
  88. 12 5
      htdocs/core/db/DoliDB.class.php
  89. 7 5
      htdocs/core/js/lib_notification.js.php
  90. 166 0
      htdocs/core/js/listview.js
  91. 92 0
      htdocs/core/lib/accounting.lib.php
  92. 1 1
      htdocs/core/lib/ajax.lib.php
  93. 49 0
      htdocs/core/lib/company.lib.php
  94. 24 6
      htdocs/core/lib/files.lib.php
  95. 82 34
      htdocs/core/lib/functions.lib.php
  96. 2 1
      htdocs/core/lib/functions2.lib.php
  97. 28 5
      htdocs/core/lib/pdf.lib.php
  98. 14 14
      htdocs/core/lib/report.lib.php
  99. 2 2
      htdocs/core/lib/security2.lib.php
  100. 15 3
      htdocs/core/menus/standard/eldy.lib.php

+ 3 - 1
ChangeLog

@@ -13,7 +13,9 @@ Following changes may create regression for some external modules, but were nece
 * The page compta/facture.php was renamed into compta/facture/card.php to match page naming conventions.
 * The signature of method ->delete() of class Product and PriceExpression was changed from 
   ->delete($id, notrigger) to ->delete(User, notrigger) to match standard dev rules.
-
+* Removed CommonObject::displayMarginInfos (was deprecated in 3.8). Use same method into
+  html.formmargin.class.php
+* Removed Societe::set_commnucation_level (was deprecated in 4.0). Was not used.
 
 ***** ChangeLog for 5.0.1 compared to 5.0.0 *****
 FIX: #6503: SQL error in "Last pending payment invoices"

+ 22 - 7
build/debian/README.howto

@@ -385,13 +385,14 @@ http://packages.qa.debian.org
 * Package will be into release when test will be moved as stable.
 
 
-##### Send an unblock request 
+
+##### Send an unblock request to make a full update of a stable package 
 
 Use this to move from unstable to testing.
 
 reportbug -B debian --smtphost=smtp.gmail.com:587 --smtpuser=xxxx --smtppasswd=yyyy --tls
 Choose package "release.debian.org"
-Then "unblock"
+Then usertag "unblock"
 Then name of package "dolibarr"
 Fill message, for example:
 "Please unblock package dolibarr
@@ -400,12 +401,11 @@ Note that package 3.5.7 contains not only fixed for bugs reported to debian. It
 so it is a better solution to validate this maintenance release than applying a patch of the only CVE-2015-3935. 
 After discussion with ..., it appears that security holes are enough to request this unblock request."
 
-
-Use this to request an update of a stable package
+Use this to request an full update of a stable package
 
 reportbug -B debian --smtphost=smtp.gmail.com:587 --smtpuser=xxxx --smtppasswd=yyyy --tls
 Choose package "release.debian.org"
-Then "unblock"
+Then usertag "unblock"
 Then name of package "dolibarr"
 Fill message, for example:
 "
@@ -417,11 +417,26 @@ Pro are:
 - It fixes also stability bugs
 - Patches were already tested because deployed and used by several thousands of users.
 - It is easier for package maintener to include this official set of fixes than applying one patch after one patch for each debian report or backported each patch into a dedicated version.
-- Debian maintenance version matches with official project maintenance version (better when all fixes are not related to the way the software is packaged)
+- Debian maintenance version is inline with official project maintenance version (better when all fixes are not related to the way the software is packaged)
 Cons are: 
-- The patch include more than the only one security reported fxes
+- The patch include more than the only one security reported fixes
 
 So I just need to know if it's ok to push such a version 3.5.7 (fixes for 3.5.* branch) instead of only one fix for only the few (the only) reported debian bugs,
 since it provides more stability and is for me a more secured process.
 "
 
+##### Send an request to ask a simple fix of a stable package 
+
+Use this to ask to apply patches on a stable version.
+
+reportbug -B debian --smtphost=smtp.gmail.com:587 --smtpuser=xxxx --smtppasswd=yyyy --tls
+Choose package "release.debian.org"
+Then usertag "jessie-pu"
+Then name of package "dolibarr"
+Fill message, for example:
+"Please unblock package dolibarr
+A security error CVE-2015-3935 was reported and is fixed into package 3.5.7.
+Note that package 3.5.7 contains not only fixed for bugs reported to debian. It includes other fixes, but they are all related to stability or security,
+so it is a better solution to validate this maintenance release than applying a patch of the only CVE-2015-3935. 
+After discussion with ..., it appears that security holes are enough to request this unblock request."
+

+ 1 - 1
build/debian/install.forced.php.install

@@ -7,7 +7,7 @@
 //
 
 $force_install_packager='deb';
-$force_install_noedit=2;
+$force_install_noedit=1;
 $force_install_message='KeepDefaultValuesDeb';
 #$force_install_main_data_root='/usr/share/dolibarr/documents';
 $force_install_main_data_root='/var/lib/dolibarr/documents';

+ 2 - 1
build/makepack-dolibarr.pl

@@ -509,7 +509,7 @@ if ($nboftargetok) {
 		$ret=`rm -fr $BUILDROOT/$PROJECT/htdocs/documents`;
 
 		# Removed known external modules to avoid any error when packaging from env where external modules are tested 
-	    $ret=`rm -fr $BUILDROOT/$PROJECT/htdocs/custom/*`;	# For custom we want to keep dir
+	    #$ret=`find $BUILDROOT/$PROJECT/htdocs/custom/* -type d -exec rm -fr {} \;`;	# For custom we want to keep dir
 		$ret=`rm -fr $BUILDROOT/$PROJECT/htdocs/allscreens*`;
 		$ret=`rm -fr $BUILDROOT/$PROJECT/htdocs/ancotec*`;
 	    $ret=`rm -fr $BUILDROOT/$PROJECT/htdocs/cabinetmed*`;
@@ -550,6 +550,7 @@ if ($nboftargetok) {
         $ret=`rm -fr $BUILDROOT/$PROJECT/htdocs/includes/mike42/escpos-php/doc`;
         $ret=`rm -fr $BUILDROOT/$PROJECT/htdocs/includes/mike42/escpos-php/example`;
         $ret=`rm -fr $BUILDROOT/$PROJECT/htdocs/includes/mike42/escpos-php/test`;
+        $ret=`rm -fr $BUILDROOT/$PROJECT/htdocs/includes/mobiledetect/mobiledetectlib/.gitmodules`;
         $ret=`rm -fr $BUILDROOT/$PROJECT/htdocs/includes/nusoap/lib/Mail`;
         $ret=`rm -fr $BUILDROOT/$PROJECT/htdocs/includes/nusoap/samples`;
         $ret=`rm -fr $BUILDROOT/$PROJECT/htdocs/includes/php-iban/docs`;

+ 3 - 4
htdocs/accountancy/admin/accountmodel.php

@@ -757,8 +757,7 @@ if ($id)
         {
         	print '<tr><td colspan="8">* '.$langs->trans("AvailableVariables").": ";
         	require_once DOL_DOCUMENT_ROOT.'/core/class/html.formmail.class.php';
-        	$formmail=new FormMail($db);
-        	$tmp=$formmail->getAvailableSubstitKey('form');
+        	$tmp=FormMail::getAvailableSubstitKey('formemail');
         	print implode(', ', $tmp);
         	print '</td></tr>';
         }
@@ -901,8 +900,8 @@ if ($id)
         if ($id == 4) print '<td></td>';
         print '<td class="liste_titre"></td>';
     	print '<td class="liste_titre" colspan="2" align="right">';
-    	$searchpitco=$form->showFilterAndCheckAddButtons(0);
-    	print $searchpitco;
+    	$searchpicto=$form->showFilterAndCheckAddButtons(0);
+    	print $searchpicto;
     	print '</td>';
         print '</tr>';
             

+ 1 - 1
htdocs/accountancy/admin/export.php

@@ -130,7 +130,7 @@ print '<form action="' . $_SERVER["PHP_SELF"] . '" method="post">';
 print '<input type="hidden" name="token" value="' . $_SESSION['newtoken'] . '">';
 print '<input type="hidden" name="action" value="update">';
 
-dol_fiche_head($head, 'export', $langs->trans("Configuration"), 0, 'cron');
+dol_fiche_head($head, 'export', $langs->trans("Configuration"), -1, 'cron');
 
 $var = true;
 

+ 2 - 2
htdocs/accountancy/admin/index.php

@@ -171,7 +171,7 @@ print '<form action="' . $_SERVER["PHP_SELF"] . '" method="post">';
 print '<input type="hidden" name="token" value="' . $_SESSION['newtoken'] . '">';
 print '<input type="hidden" name="action" value="update">';
 
-dol_fiche_head($head, 'general', $langs->trans("Configuration"), 0, 'cron');
+dol_fiche_head($head, 'general', $langs->trans("Configuration"), -1, 'cron');
 
 
 // Default mode for calculating turnover (parameter ACCOUNTING_MODE)
@@ -299,7 +299,7 @@ print '<div class="center"><input type="submit" class="button" value="' . $langs
 print '<br>';
 print '<br>';
 
-print $langs->trans("AccountancySetupDoneFromAccountancyMenu", $langs->transnoentitiesnoconv("Home").'-'.$langs->transnoentitiesnoconv("MenuFinancial").'-'.$langs->transnoentitiesnoconv("MenuAccountancy"));
+print '<div class="opacitymedium">'.$langs->trans("AccountancySetupDoneFromAccountancyMenu", $langs->transnoentitiesnoconv("MenuFinancial").'-'.$langs->transnoentitiesnoconv("MenuAccountancy")).'</div>';
 
 print '<br>';
 print '</form>';

+ 1 - 1
htdocs/accountancy/admin/journals.php

@@ -94,7 +94,7 @@ print load_fiche_titre($langs->trans('ConfigAccountingExpert'), $linkback, 'titl
 
 $head = admin_accounting_prepare_head(null);
 
-dol_fiche_head($head, 'journal', $langs->trans("Configuration"), 0, 'cron');
+dol_fiche_head($head, 'journal', $langs->trans("Configuration"), -1, 'cron');
 
 $sql = "SELECT j.rowid, j.code, j.label, j.nature, j.active";
 $sql .= " FROM " . MAIN_DB_PREFIX . "accounting_journal as j";

+ 6 - 2
htdocs/accountancy/bookkeeping/card.php

@@ -305,6 +305,8 @@ if ($action == 'create') {
 
 		dol_fiche_head();
 		
+		print '<div class="fichecenter">';
+		
 		print '<table class="border" width="100%">';
 		print '<tr class="pair">';
 		print '<td class="titlefield">' . $langs->trans("NumMvts") . '</td>';
@@ -328,6 +330,8 @@ if ($action == 'create') {
 		print '</tr>';
 		print '</table>';
 		
+		print '</div>';
+		
 		dol_fiche_end();
 
 		print '<br>';
@@ -427,8 +431,8 @@ if ($action == 'create') {
 					print $formventilation->select_auxaccount($code_tiers, 'code_tiers', 1);
 					print '</td>';
 					print '<td><input type="text" size="15" name="label_compte" value="' . $label_compte . '"/></td>';
-					print '<td align="right"><input type="text" size="6" name="debit" value="' . price($debit) . '"/></td>';
-					print '<td align="right"><input type="text" size="6" name="credit" value="' . price($credit) . '"/></td>';
+					print '<td align="right"><input type="text" class="right maxwidth50" name="debit" value="' . price($debit) . '"/></td>';
+					print '<td align="right"><input type="text" class="right maxwidth50" name="credit" value="' . price($credit) . '"/></td>';
 					print '<td></td>';
 					print '<td></td>';
 					print '<td><input type="submit" class="button" name="save" value="' . $langs->trans("Add") . '"></td>';

+ 2 - 2
htdocs/accountancy/bookkeeping/list.php

@@ -360,7 +360,7 @@ if (count($filter)) $button.= $langs->trans("ExportFilteredList");
 else $button.= $langs->trans("ExportList");
 $button.= '</a>';
 
-$groupby = ' <a href="./listbyaccount.php">' . $langs->trans("GroupByAccountAccounting") . '</a>';
+$groupby = ' <a class="nohover" href="'.DOL_URL_ROOT.'/accountancy/bookkeeping/listbyaccount.php"">' . $langs->trans("GroupByAccountAccounting") . '</a>';
 
 print_barre_liste($title_page, $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, $button, $result, $nbtotalofrecords, 'title_accountancy', 0, $groupby, '', $limit);
 
@@ -437,7 +437,7 @@ foreach ($object->lines as $line ) {
 
 	print '<td><a href="./card.php?piece_num=' . $line->piece_num . '">' . $line->piece_num . '</a></td>';
 	print '<td align="center">' . dol_print_date($line->doc_date, 'day') . '</td>';
-	print '<td>' . $line->doc_ref . '</td>';
+	print '<td class="nowrap">' . $line->doc_ref . '</td>';
 	print '<td>' . length_accountg($line->numero_compte) . '</td>';
 	print '<td>' . length_accounta($line->code_tiers) . '</td>';
 	print '<td>' . $line->label_compte . '</td>';

+ 26 - 22
htdocs/accountancy/bookkeeping/listbyaccount.php

@@ -207,7 +207,7 @@ if ($action == 'delbookkeepingyear') {
 
 print '<form method="GET" id="searchFormList" action="' . $_SERVER["PHP_SELF"] . '">';
 
-$viewflat = ' <a href="./list.php">' . $langs->trans("ViewFlatList") . '</a>';
+$viewflat = ' <a class="nohover" href="'.DOL_URL_ROOT.'/accountancy/bookkeeping/list.php">' . $langs->trans("ViewFlatList") . '</a>';
 
 print_barre_liste($title_page, $page, $_SERVER["PHP_SELF"], $options, $sortfield, $sortorder, '', $result, $nbtotalofrecords,'title_accountancy',0,$viewflat,'',$limit);
 
@@ -222,17 +222,6 @@ print '<div class="inline-block divButAction"><a class="butAction" href="./card.
 print '</div>';
 
 print '<table class="noborder" width="100%">';
-print '<tr class="liste_titre">';
-print '<td>' . $langs->trans("AccountAccounting") . '</td>';
-print_liste_field_titre($langs->trans("TransactionNumShort"), $_SERVER['PHP_SELF'], "t.piece_num", "", $options, 'align="right"', $sortfield, $sortorder);
-print_liste_field_titre($langs->trans("Docdate"), $_SERVER['PHP_SELF'], "t.doc_date", "", $options, 'align="center"', $sortfield, $sortorder);
-print_liste_field_titre($langs->trans("Docref"), $_SERVER['PHP_SELF'], "t.doc_ref", "", $options, "", $sortfield, $sortorder);
-print_liste_field_titre($langs->trans("SuppliersInvoices") . ' / ' . $langs->trans("CustomersInvoices"));
-print_liste_field_titre($langs->trans("Debit"), $_SERVER['PHP_SELF'], "t.debit", "", $options, 'align="right"', $sortfield, $sortorder);
-print_liste_field_titre($langs->trans("Credit"), $_SERVER['PHP_SELF'], "t.credit", "", $options, 'align="right"', $sortfield, $sortorder);
-print_liste_field_titre($langs->trans("Codejournal"), $_SERVER['PHP_SELF'], "t.code_journal", "", $options, 'align="right"', $sortfield, $sortorder);
-print_liste_field_titre('', $_SERVER["PHP_SELF"], "", $options, "", 'width="60" align="center"', $sortfield, $sortorder);
-print "</tr>\n";
 
 print '<tr class="liste_titre">';
 print '<td class="liste_titre">' . $object->select_account($search_accountancy_code_start, 'search_accountancy_code_start', 1, array (), 1, 1, '') . '</td>';
@@ -254,6 +243,18 @@ $searchpitco=$form->showFilterAndCheckAddButtons(0);
 print $searchpitco;
 print '</td>';
 
+print '<tr class="liste_titre">';
+print_liste_field_titre($langs->trans("AccountAccountingShort"), $_SERVER['PHP_SELF']);
+print_liste_field_titre($langs->trans("TransactionNumShort"), $_SERVER['PHP_SELF'], "t.piece_num", "", $options, 'align="right"', $sortfield, $sortorder);
+print_liste_field_titre($langs->trans("Docdate"), $_SERVER['PHP_SELF'], "t.doc_date", "", $options, 'align="center"', $sortfield, $sortorder);
+print_liste_field_titre($langs->trans("Docref"), $_SERVER['PHP_SELF'], "t.doc_ref", "", $options, "", $sortfield, $sortorder);
+print_liste_field_titre($langs->trans("Label"));
+print_liste_field_titre($langs->trans("Debit"), $_SERVER['PHP_SELF'], "t.debit", "", $options, 'align="right"', $sortfield, $sortorder);
+print_liste_field_titre($langs->trans("Credit"), $_SERVER['PHP_SELF'], "t.credit", "", $options, 'align="right"', $sortfield, $sortorder);
+print_liste_field_titre($langs->trans("Codejournal"), $_SERVER['PHP_SELF'], "t.code_journal", "", $options, 'align="right"', $sortfield, $sortorder);
+print_liste_field_titre('', $_SERVER["PHP_SELF"], "", $options, "", 'width="60" align="center"', $sortfield, $sortorder);
+print "</tr>\n";
+
 print '</tr>';
 
 $var = True;
@@ -277,9 +278,10 @@ foreach ( $object->lines as $line ) {
         
         // Affiche un Sous-Total par compte comptable
         if (isset($displayed_account_number)) {
-          print '<tr class="liste_total"><td align="right" colspan="4">'.$langs->trans("SubTotal").':</td><td class="nowrap" align="right">'.price($sous_total_debit).'</td><td class="nowrap" align="right">'.price($sous_total_credit).'</td>';
-          print "<td>&nbsp;</td>\n";
-          print '</tr>';
+            print '<tr class="liste_total"><td align="right" colspan="5">'.$langs->trans("SubTotal").':</td><td class="nowrap" align="right">'.price($sous_total_debit).'</td><td class="nowrap" align="right">'.price($sous_total_credit).'</td>';
+            print "<td>&nbsp;</td>\n";
+            print "<td>&nbsp;</td>\n";
+            print '</tr>';
         }
         
         // Show the break account
@@ -299,17 +301,18 @@ foreach ( $object->lines as $line ) {
 
 	print '<tr class="oddeven">';
 	print '<td>&nbsp;</td>';
-	print '<td align="right">'.$line->piece_num.'</td>';
+	print '<td align="right"><a href="./card.php?piece_num=' . $line->piece_num . '">'.$line->piece_num.'</a></td>';
 	print '<td align="center">' . dol_print_date($line->doc_date, 'day') . '</td>';
-	print '<td><a href="./card.php?piece_num=' . $line->piece_num . '">' . $line->doc_ref . '</a></td>';
+	
+	// TODO Add a link according to doc_type and fk_doc
+	print '<td class="nowrap">';
+    //if ($line->doc_type == 'supplier_invoice')
+    //if ($line->doc_type == 'customer_invoice')
+	print $line->doc_ref;
+    print '</td>';
     
     // Affiche un lien vers la facture client/fournisseur
     $doc_ref = preg_replace('/\(.*\)/', '', $line->doc_ref);
-    if ($line->doc_type == 'supplier_invoice')
-     print strlen(length_accounta($line->code_tiers)) == 0 ? '<td><a href="/fourn/facture/list.php?search_ref_supplier=' . $doc_ref . '">' . $line->label_compte . '</a></td>' : '<td><a href="/fourn/facture/list.php?search_ref_supplier=' . $doc_ref . '">' . $line->label_compte . '</a><br /><span style="font-size:0.8em">(' . length_accounta($line->code_tiers) . ')</span></td>';
-    elseif ($line->doc_type == 'customer_invoice')
-    print strlen(length_accounta($line->code_tiers)) == 0 ? '<td><a href="/compta/facture/list.php?search_ref=' . $doc_ref . '">' . $line->label_compte . '</a></td>' : '<td><a href="/compta/facture/list.php?search_ref=' . $doc_ref . '">' . $line->label_compte . '</a><br /><span style="font-size:0.8em">(' . length_accounta($line->code_tiers) . ')</span></td>';
-    else
     print strlen(length_accounta($line->code_tiers)) == 0 ? '<td>' . $line->label_compte . '</td>' : '<td>' . $line->label_compte . '<br /><span style="font-size:0.8em">(' . length_accounta($line->code_tiers) . ')</span></td>';
 
 
@@ -332,6 +335,7 @@ foreach ( $object->lines as $line ) {
 print '<tr class="liste_total">';
 print '<td align="right" colspan="5">'.$langs->trans("SubTotal").':</td><td class="nowrap" align="right">'.price($sous_total_debit).'</td><td class="nowrap" align="right">'.price($sous_total_credit).'</td>';
 print "<td>&nbsp;</td>\n";
+print "<td>&nbsp;</td>\n";
 print '</tr>';
 
 

+ 12 - 3
htdocs/accountancy/class/bookkeeping.class.php

@@ -169,7 +169,15 @@ class BookKeeping extends CommonObject
 		if (empty($this->numero_compte) || $this->numero_compte == '-1')
 		{
 		    $langs->load("errors");
-            $this->errors[]=$langs->trans('ErrorFieldAccountNotDefinedForBankLine', $this->fk_docdet);		    
+            if (in_array($this->doc_type, array('bank', 'expense_report')))
+            {
+		        $this->errors[]=$langs->trans('ErrorFieldAccountNotDefinedForBankLine', $this->fk_docdet,  $this->doc_type);
+            }
+            else
+            {
+                $this->errors[]=$langs->trans('ErrorFieldAccountNotDefinedForInvoiceLine', $this->fk_doc,  $this->doc_type);		    
+            }
+		    
 		    return -1;
 		}
 		
@@ -178,11 +186,12 @@ class BookKeeping extends CommonObject
 		
 		$this->piece_num = 0;
 		
-		// first check if line not yet in bookkeeping
+		// First check if line not yet already in bookkeeping
 		$sql = "SELECT count(*) as nb";
 		$sql .= " FROM " . MAIN_DB_PREFIX . $this->table_element;
 		$sql .= " WHERE doc_type = '" . $this->doc_type . "'";
-		$sql .= " AND fk_docdet = " . $this->fk_docdet;
+		$sql .= " AND fk_doc = " . $this->fk_doc;
+		$sql .= " AND fk_docdet = " . $this->fk_docdet;                   // This field can be 0 is record is for several lines 
 		$sql .= " AND numero_compte = '" . $this->numero_compte . "'";
 	    $sql .= " AND entity IN (" . getEntity("accountancy", 1) . ")";
 		

+ 12 - 4
htdocs/accountancy/customer/lines.php

@@ -43,6 +43,7 @@ $langs->load("productbatch");
 $account_parent = GETPOST('account_parent');
 $changeaccount = GETPOST('changeaccount');
 // Search Getpost
+$search_lineid = GETPOST('search_lineid', 'int');
 $search_ref = GETPOST('search_ref', 'alpha');
 $search_invoice = GETPOST('search_invoice', 'alpha');
 $search_label = GETPOST('search_label', 'alpha');
@@ -86,6 +87,7 @@ $formventilation = new FormVentilation($db);
 // Purge search criteria
 if (GETPOST("button_removefilter_x") || GETPOST("button_removefilter.x") || GETPOST("button_removefilter")) // All tests are required to be compatible with all browsers
 {
+    $search_lineid = '';
 	$search_ref = '';
 	$search_invoice = '';
 	$search_label = '';
@@ -151,7 +153,7 @@ print '<script type="text/javascript">
  * Customer Invoice lines
  */
 $sql = "SELECT f.rowid, f.facnumber, f.type, f.datef, f.ref_client,";
-$sql .= " fd.rowid as fdid, fd.description, fd.product_type, fd.total_ht, fd.total_tva, fd.tva_tx, fd.total_ttc,";
+$sql .= " fd.rowid, fd.description, fd.product_type, fd.total_ht, fd.total_tva, fd.tva_tx, fd.total_ttc,";
 $sql .= " s.rowid as socid, s.nom as name, s.code_compta, s.code_client,";
 $sql .= " p.rowid as product_id, p.ref as product_ref, p.label as product_label, p.accountancy_code_sell, aa.rowid as fk_compte, aa.account_number, aa.label as label_compte,";
 $sql .= " fd.situation_percent, co.label as country, s.tva_intra";
@@ -169,6 +171,9 @@ if (! empty($conf->global->FACTURE_DEPOSITS_ARE_JUST_PAYMENTS)) {
 } else {
 	$sql .= " AND f.type IN (" . Facture::TYPE_STANDARD . "," . Facture::TYPE_STANDARD . "," . Facture::TYPE_CREDIT_NOTE . "," . Facture::TYPE_DEPOSIT . "," . Facture::TYPE_SITUATION . ")";
 }
+if ($search_lineid) {
+    $sql .= natural_search("fd.rowid", $search_lineid, 1);
+}
 if (strlen(trim($search_invoice))) {
 	$sql .= natural_search("f.facnumber", $search_invoice);
 }
@@ -257,7 +262,7 @@ if ($result) {
 	print '<table class="tagtable liste'.($moreforfilter?" listwithfilterbefore":"").'">'."\n";
 	
 	print '<tr class="liste_titre_filter">';
-	print '<td class="liste_titre"></td>';
+	print '<td class="liste_titre"><input type="text" class="flat maxwidth50" name="search_lineid" value="' . dol_escape_htmltag($search_lineid) . '""></td>';
 	print '<td class="liste_titre"><input type="text" class="flat maxwidth50" name="search_invoice" value="' . dol_escape_htmltag($search_invoice) . '"></td>';
 	print '<td class="liste_titre"></td>';
 	print '<td class="liste_titre"><input type="text" class="flat maxwidth50" name="search_ref" value="' . dol_escape_htmltag($search_ref) . '"></td>';
@@ -328,13 +333,16 @@ if ($result) {
 		print '<td align="right">' . price($objp->total_ht) . '</td>';
 		print '<td align="center">' . price($objp->tva_tx) . '</td>';
 		print '<td>';
-		print $codecompta . ' <a href="./card.php?id=' . $objp->fdid . '">';
+		print $codecompta . ' <a href="./card.php?id=' . $objp->rowid . '">';
 		print img_edit();
 		print '</a>';
 		print '</td>';
+		
 		print '<td>' . $objp->country .'</td>';
+		
 		print '<td>' . $objp->tva_intra . '</td>';
-		print '<td class="center"><input type="checkbox" class="checkforaction" name="changeaccount[]" value="' . $objp->fdid . '"/></td>';
+		
+		print '<td class="center"><input type="checkbox" class="checkforaction" name="changeaccount[]" value="' . $objp->rowid . '"/></td>';
 
 		print "</tr>";
 		$i ++;

+ 9 - 4
htdocs/accountancy/customer/list.php

@@ -52,6 +52,7 @@ $toselect = GETPOST('toselect', 'array');
 $mesCasesCochees = GETPOST('toselect', 'array');
 
 // Search Getpost
+$search_lineid = GETPOST('search_lineid', 'int');
 $search_invoice = GETPOST('search_invoice', 'alpha');
 $search_ref = GETPOST('search_ref', 'alpha');
 $search_label = GETPOST('search_label', 'alpha');
@@ -100,6 +101,7 @@ if (! GETPOST('confirmmassaction') && $massaction != 'presend' && $massaction !=
 // 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_lineid = '';
 	$search_ref = '';
 	$search_invoice = '';
 	$search_label = '';
@@ -177,7 +179,7 @@ llxHeader('', $langs->trans("Ventilation"));
 
 // Customer Invoice lines
 $sql = "SELECT f.facnumber, f.rowid as facid, f.datef, f.type as ftype,";
-$sql .= " l.fk_product, l.description, l.total_ht, l.rowid, l.fk_code_ventilation, l.product_type as type_l, l.tva_tx as tva_tx_line,";
+$sql .= " l.rowid, l.fk_product, l.description, l.total_ht, l.fk_code_ventilation, l.product_type as type_l, l.tva_tx as tva_tx_line,";
 $sql .= " p.rowid as product_id, p.ref as product_ref, p.label as product_label, p.fk_product_type as type, p.accountancy_code_sell as code_sell, p.tva_tx as tva_tx_prod,";
 $sql .= " aa.rowid as aarowid";
 $sql .= " FROM " . MAIN_DB_PREFIX . "facture as f";
@@ -189,6 +191,9 @@ $sql .= " WHERE f.fk_statut > 0 AND l.fk_code_ventilation <= 0";
 $sql .= " AND product_type <= 2";
 $sql .= " AND (accsys.rowid='" . $conf->global->CHARTOFACCOUNTS . "' OR p.accountancy_code_sell IS NULL OR p.accountancy_code_sell ='')";
 // Add search filter like
+if ($search_lineid) {
+    $sql .= natural_search("l.rowid", $search_lineid, 1);
+}
 if (strlen(trim($search_invoice))) {
     $sql .= natural_search("f.facnumber",$search_invoice);
 }
@@ -274,7 +279,7 @@ if ($result) {
 
 	// We add search filter
 	print '<tr class="liste_titre_filter">';
-	print '<td class="liste_titre"></td>';
+	print '<td class="liste_titre"><input type="text" class="flat maxwidth50" name="search_lineid" value="' . dol_escape_htmltag($search_lineid) . '""></td>';
 	print '<td class="liste_titre"><input type="text" class="flat maxwidth50" name="search_invoice" value="' . dol_escape_htmltag($search_invoice) . '"></td>';
 	print '<td class="liste_titre"></td>';
 	print '<td class="liste_titre"><input type="text" class="flat maxwidth50" name="search_ref" value="' . dol_escape_htmltag($search_ref) . '"></td>';
@@ -285,7 +290,7 @@ if ($result) {
 	print '<td class="liste_titre"></td>';
 	print '<td class="liste_titre"></td>';
 	print '<td align="center" class="liste_titre">';
-	$searchpicto=$form->showFilterAndCheckAddButtons($massactionbutton?1:0, 'checkforselect', 1);
+	$searchpicto=$form->showFilterButtons();
 	print $searchpicto;
 	print '</td>';
 	print '</tr>';
@@ -303,7 +308,7 @@ if ($result) {
 	print_liste_field_titre($langs->trans("IntoAccount"), '', '', '', '', 'align="center"');
 	$checkpicto='';
 	if ($massactionbutton) $checkpicto=$form->showCheckAddButtons('checkforselect', 1);
-	print_liste_field_titre($checkpitco, '', '', '', '', 'align="center"');
+	print_liste_field_titre($checkpicto, '', '', '', '', 'align="center"');
 	print "</tr>\n";
 	
 	$facture_static = new Facture($db);

+ 6 - 3
htdocs/accountancy/index.php

@@ -129,15 +129,18 @@ if (! empty($conf->don->enabled))
     print "<br>\n";
     print "<br>\n";
 }*/
+
 $step++;
-$textlink='<strong>'.$langs->transnoentitiesnoconv("Home").'-'.$langs->transnoentitiesnoconv("MenuBankCash").'</strong>';
-print img_picto('', 'puce').' '.$langs->trans("AccountancyAreaDescBank", $step, $textlink);
+print img_picto('', 'puce').' '.$langs->trans("AccountancyAreaDescProd", $step, '<strong>'.$langs->transnoentitiesnoconv("MenuFinancial").'-'.$langs->transnoentitiesnoconv("MenuAccountancy").'-'.$langs->transnoentitiesnoconv("Setup")."-".$langs->transnoentitiesnoconv("ProductsBinding").'</strong>');
 print "<br>\n";
 print "<br>\n";
 
 $step++;
-print img_picto('', 'puce').' '.$langs->trans("AccountancyAreaDescProd", $step, '<strong>'.$langs->transnoentitiesnoconv("MenuFinancial").'-'.$langs->transnoentitiesnoconv("MenuAccountancy").'-'.$langs->transnoentitiesnoconv("Setup")."-".$langs->transnoentitiesnoconv("ProductsBinding").'</strong>')."<br>\n";
+$textlink='<strong>'.$langs->transnoentitiesnoconv("MenuBankCash").'</strong>';
+print img_picto('', 'puce').' '.$langs->trans("AccountancyAreaDescBank", $step, $textlink);
 print "<br>\n";
+print "<br>\n";
+
 
 print "<br>\n";
 print_fiche_titre($langs->trans("AccountancyAreaDescActionFreq"), '', 'object_calendarweek');

+ 153 - 138
htdocs/accountancy/journal/bankjournal.php

@@ -106,7 +106,7 @@ if (empty($date_start) || empty($date_end)) // We define date_start and date_end
 $p = explode(":", $conf->global->MAIN_INFO_SOCIETE_COUNTRY);
 $idpays = $p[0];
 
-$sql  = "SELECT b.rowid , b.dateo as do, b.datev as dv, b.amount, b.label, b.rappro, b.num_releve, b.num_chq, b.fk_type,";
+$sql  = "SELECT b.rowid, b.dateo as do, b.datev as dv, b.amount, b.label, b.rappro, b.num_releve, b.num_chq, b.fk_type,";
 $sql .= " ba.courant, ba.ref as baref, ba.account_number,";
 $sql .= " soc.code_compta, soc.code_compta_fournisseur, soc.rowid as socid, soc.nom as name, bu1.type as typeop,";
 $sql .= " u.accountancy_code, u.rowid as userid, u.lastname as name, u.firstname as firstname, bu2.type as typeop";
@@ -341,79 +341,81 @@ if (! $error && $action == 'writebookkeeping') {
 		    // Line into bank account
     		foreach ( $tabbq[$key] as $k => $mt ) 
     		{
-    			$bookkeeping = new BookKeeping($db);
-    			$bookkeeping->doc_date = $val["date"];
-    			$bookkeeping->doc_ref = $val["ref"];
-    			$bookkeeping->doc_type = 'bank';
-    			$bookkeeping->fk_doc = $key;
-    			$bookkeeping->fk_docdet = $val["fk_bank"];
-    			$bookkeeping->numero_compte = $k;
-    			$bookkeeping->label_compte = $compte->label;
-    			$bookkeeping->montant = ($mt < 0 ? - $mt : $mt);
-    			$bookkeeping->sens = ($mt >= 0) ? 'D' : 'C';
-    			$bookkeeping->debit = ($mt >= 0 ? $mt : 0);
-    			$bookkeeping->credit = ($mt < 0 ? - $mt : 0);
-    			$bookkeeping->code_journal = $journal;
-    			$bookkeeping->fk_user_author = $user->id;
-    			$bookkeeping->date_create = $now;
+    			if ($mt) {
+        		    $bookkeeping = new BookKeeping($db);
+        			$bookkeeping->doc_date = $val["date"];
+        			$bookkeeping->doc_ref = $val["ref"];
+        			$bookkeeping->doc_type = 'bank';
+        			$bookkeeping->fk_doc = $key;
+        			$bookkeeping->fk_docdet = $val["fk_bank"];
+        			$bookkeeping->numero_compte = $k;
+        			$bookkeeping->label_compte = $compte->label;
+        			$bookkeeping->montant = ($mt < 0 ? - $mt : $mt);
+        			$bookkeeping->sens = ($mt >= 0) ? 'D' : 'C';
+        			$bookkeeping->debit = ($mt >= 0 ? $mt : 0);
+        			$bookkeeping->credit = ($mt < 0 ? - $mt : 0);
+        			$bookkeeping->code_journal = $journal;
+        			$bookkeeping->fk_user_author = $user->id;
+        			$bookkeeping->date_create = $now;
+        
+        			if ($tabtype[$key] == 'payment') {
+        			    $bookkeeping->code_tiers = $tabcompany[$key]['code_compta'];
     
-    			if ($tabtype[$key] == 'payment') {
-    			    $bookkeeping->code_tiers = $tabcompany[$key]['code_compta'];
-
-    				$sqlmid = 'SELECT fac.facnumber';
-    				$sqlmid .= " FROM " . MAIN_DB_PREFIX . "facture fac";
-    				$sqlmid .= " INNER JOIN " . MAIN_DB_PREFIX . "paiement_facture as payfac ON payfac.fk_facture=fac.rowid";
-    				$sqlmid .= " INNER JOIN " . MAIN_DB_PREFIX . "paiement as pay ON  payfac.fk_paiement=pay.rowid";
-    				$sqlmid .= " WHERE pay.fk_bank=" . $key;
-    				dol_syslog("accountancy/journal/bankjournal.php:: sqlmid=" . $sqlmid, LOG_DEBUG);
-    				$resultmid = $db->query($sqlmid);
-    				if ($resultmid) {
-    					$objmid = $db->fetch_object($resultmid);
-    					$bookkeeping->doc_ref = $objmid->facnumber;    // Ref of invoice
-    				}
-    			} else if ($tabtype[$key] == 'payment_supplier') {
-    			    $bookkeeping->code_tiers = $tabcompany[$key]['code_compta'];
-
-    				$sqlmid = 'SELECT facf.ref_supplier, facf.ref';
-    				$sqlmid .= " FROM " . MAIN_DB_PREFIX . "facture_fourn facf";
-    				$sqlmid .= " INNER JOIN " . MAIN_DB_PREFIX . "paiementfourn_facturefourn as payfacf ON payfacf.fk_facturefourn=facf.rowid";
-    				$sqlmid .= " INNER JOIN " . MAIN_DB_PREFIX . "paiementfourn as payf ON  payfacf.fk_paiementfourn=payf.rowid";
-    				$sqlmid .= " WHERE payf.fk_bank=" . $key;
-    				dol_syslog("accountancy/journal/bankjournal.php:: sqlmid=" . $sqlmid, LOG_DEBUG);
-    				$resultmid = $db->query($sqlmid);
-    				if ($resultmid) {
-    					$objmid = $db->fetch_object($resultmid);
-    					$bookkeeping->doc_ref = $objmid->ref_supplier . ' (' . $objmid->ref . ')'; // Ref on invoice
-    				}
-    			} else if ($tabtype[$key] == 'payment_expensereport') {
-    			    $bookkeeping->code_tiers = $tabuser[$key]['accountancy_code'];
-
-    				$sqlmid = 'SELECT e.ref';
-    				$sqlmid .= " FROM " . MAIN_DB_PREFIX . "expensereport as e";
-    				$sqlmid .= " INNER JOIN " . MAIN_DB_PREFIX . "payment_expensereport as payer ON payer.fk_expensereport=e.rowid";
-    				$sqlmid .= " WHERE payer.fk_expensereport=" . $val["fk_expensereport"];
-    				dol_syslog("accountancy/journal/bankjournal.php:: sqlmid=" . $sqlmid, LOG_DEBUG);
-    				$resultmid = $db->query($sqlmid);
-    				if ($resultmid) {
-    					$objmid = $db->fetch_object($resultmid);
-    					$bookkeeping->doc_ref = $objmid->ref; // Ref of expensereport
-    				}
-    			}
+        				$sqlmid = 'SELECT fac.facnumber';
+        				$sqlmid .= " FROM " . MAIN_DB_PREFIX . "facture fac";
+        				$sqlmid .= " INNER JOIN " . MAIN_DB_PREFIX . "paiement_facture as payfac ON payfac.fk_facture=fac.rowid";
+        				$sqlmid .= " INNER JOIN " . MAIN_DB_PREFIX . "paiement as pay ON  payfac.fk_paiement=pay.rowid";
+        				$sqlmid .= " WHERE pay.fk_bank=" . $key;
+        				dol_syslog("accountancy/journal/bankjournal.php:: sqlmid=" . $sqlmid, LOG_DEBUG);
+        				$resultmid = $db->query($sqlmid);
+        				if ($resultmid) {
+        					$objmid = $db->fetch_object($resultmid);
+        					$bookkeeping->doc_ref = $objmid->facnumber;    // Ref of invoice
+        				}
+        			} else if ($tabtype[$key] == 'payment_supplier') {
+        			    $bookkeeping->code_tiers = $tabcompany[$key]['code_compta'];
+    
+        				$sqlmid = 'SELECT facf.ref_supplier, facf.ref';
+        				$sqlmid .= " FROM " . MAIN_DB_PREFIX . "facture_fourn facf";
+        				$sqlmid .= " INNER JOIN " . MAIN_DB_PREFIX . "paiementfourn_facturefourn as payfacf ON payfacf.fk_facturefourn=facf.rowid";
+        				$sqlmid .= " INNER JOIN " . MAIN_DB_PREFIX . "paiementfourn as payf ON  payfacf.fk_paiementfourn=payf.rowid";
+        				$sqlmid .= " WHERE payf.fk_bank=" . $key;
+        				dol_syslog("accountancy/journal/bankjournal.php:: sqlmid=" . $sqlmid, LOG_DEBUG);
+        				$resultmid = $db->query($sqlmid);
+        				if ($resultmid) {
+        					$objmid = $db->fetch_object($resultmid);
+        					$bookkeeping->doc_ref = $objmid->ref_supplier . ' (' . $objmid->ref . ')'; // Ref on invoice
+        				}
+        			} else if ($tabtype[$key] == 'payment_expensereport') {
+        			    $bookkeeping->code_tiers = $tabuser[$key]['accountancy_code'];
     
-    			$result = $bookkeeping->create($user);
-    			if ($result < 0) {
-    				if ($bookkeeping->error == 'BookkeepingRecordAlreadyExists')	// Already exists
-    				{
-    					$error++;
-    					$errorforline++;
-    					//setEventMessages('Transaction for ('.$bookkeeping->doc_type.', '.$bookkeeping->doc_ref.', '.$bookkeeping->fk_docdet.') were already recorded', null, 'warnings');
-    				}
-    				else
-    				{
-	    				$error++;
-	    				$errorforline++;
-	    				setEventMessages($bookkeeping->error, $bookkeeping->errors, 'errors');
-    				}
+        				$sqlmid = 'SELECT e.ref';
+        				$sqlmid .= " FROM " . MAIN_DB_PREFIX . "expensereport as e";
+        				$sqlmid .= " INNER JOIN " . MAIN_DB_PREFIX . "payment_expensereport as payer ON payer.fk_expensereport=e.rowid";
+        				$sqlmid .= " WHERE payer.fk_expensereport=" . $val["fk_expensereport"];
+        				dol_syslog("accountancy/journal/bankjournal.php:: sqlmid=" . $sqlmid, LOG_DEBUG);
+        				$resultmid = $db->query($sqlmid);
+        				if ($resultmid) {
+        					$objmid = $db->fetch_object($resultmid);
+        					$bookkeeping->doc_ref = $objmid->ref; // Ref of expensereport
+        				}
+        			}
+        
+        			$result = $bookkeeping->create($user);
+        			if ($result < 0) {
+        				if ($bookkeeping->error == 'BookkeepingRecordAlreadyExists')	// Already exists
+        				{
+        					$error++;
+        					$errorforline++;
+        					//setEventMessages('Transaction for ('.$bookkeeping->doc_type.', '.$bookkeeping->doc_ref.', '.$bookkeeping->fk_docdet.') were already recorded', null, 'warnings');
+        				}
+        				else
+        				{
+    	    				$error++;
+    	    				$errorforline++;
+    	    				setEventMessages($bookkeeping->error, $bookkeeping->errors, 'errors');
+        				}
+        			}
     			}
     		}
 		}
@@ -423,65 +425,76 @@ if (! $error && $action == 'writebookkeeping') {
 		{
 		    // Line into thirdparty account
     		foreach ( $tabtp[$key] as $k => $mt ) {
-    			$bookkeeping = new BookKeeping($db);
-    			$bookkeeping->doc_date = $val["date"];
-    			$bookkeeping->doc_ref = $val["ref"];
-    			$bookkeeping->doc_type = 'bank';
-    			$bookkeeping->fk_doc = $key;
-    			$bookkeeping->fk_docdet = $val["fk_bank"];
-    			$bookkeeping->label_compte = $tabcompany[$key]['name'];
-    			$bookkeeping->montant = ($mt < 0 ? - $mt : $mt);
-    			$bookkeeping->sens = ($mt < 0) ? 'D' : 'C';
-    			$bookkeeping->debit = ($mt < 0 ? - $mt : 0);
-    			$bookkeeping->credit = ($mt >= 0) ? $mt : 0;
-    			$bookkeeping->code_journal = $journal;
-    			$bookkeeping->fk_user_author = $user->id;
-    			$bookkeeping->date_create = $now;
-    
-    			if (in_array($tabtype[$key], array('sc', 'payment_sc'))) {   // If payment is payment of social contribution
-    				$bookkeeping->code_tiers = '';
-    				$bookkeeping->numero_compte = $k;
-    			} else if ($tabtype[$key] == 'payment') {    // If payment is payment of customer invoice, we get ref of invoice
-    				$sqlmid = 'SELECT fac.facnumber';
-    				$sqlmid .= " FROM " . MAIN_DB_PREFIX . "facture fac ";
-    				$sqlmid .= " INNER JOIN " . MAIN_DB_PREFIX . "paiement_facture as payfac ON  payfac.fk_facture=fac.rowid";
-    				$sqlmid .= " INNER JOIN " . MAIN_DB_PREFIX . "paiement as pay ON  payfac.fk_paiement=pay.rowid";
-    				$sqlmid .= " WHERE pay.fk_bank=" . $key;
-    				dol_syslog("accountancy/journal/bankjournal.php:: sqlmid=" . $sqlmid, LOG_DEBUG);
-    				$resultmid = $db->query($sqlmid);
-    				if ($resultmid) {
-    					$objmid = $db->fetch_object($resultmid);
-    					$bookkeeping->doc_ref = $objmid->facnumber;
-    				}
-    				$bookkeeping->code_tiers = $tabcompany[$key]['code_compta'];
-    				$bookkeeping->numero_compte = $k;
-    			} else if ($tabtype[$key] == 'payment_supplier') {           // If payment is payment of supplier invoice, we get ref of invoice
-    
-    				$sqlmid = 'SELECT facf.ref_supplier,facf.ref';
-    				$sqlmid .= " FROM " . MAIN_DB_PREFIX . "facture_fourn facf ";
-    				$sqlmid .= " INNER JOIN " . MAIN_DB_PREFIX . "paiementfourn_facturefourn as payfacf ON  payfacf.fk_facturefourn=facf.rowid";
-    				$sqlmid .= " INNER JOIN " . MAIN_DB_PREFIX . "paiementfourn as payf ON  payfacf.fk_paiementfourn=payf.rowid";
-    				$sqlmid .= " WHERE payf.fk_bank=" . $key;
-    				dol_syslog("accountancy/journal/bankjournal.php:: sqlmid=" . $sqlmid, LOG_DEBUG);
-    				$resultmid = $db->query($sqlmid);
-    				if ($resultmid) {
-    					$objmid = $db->fetch_object($resultmid);
-    					$bookkeeping->doc_ref = $objmid->ref_supplier . ' (' . $objmid->ref . ')';
-    				}
-                    $bookkeeping->code_tiers = $tabcompany[$key]['code_compta'];
-    				$bookkeeping->numero_compte = $k;
-    			} else {
-    			    // FIXME Should be a temporary account ???
-    				$bookkeeping->doc_ref = $k;
-    				//$bookkeeping->numero_compte = $conf->global->ACCOUNTING_ACCOUNT_CUSTOMER;
-    				$bookkeeping->numero_compte = 'CodeNotDef';
-    			}
-    
-    			$result = $bookkeeping->create($user);
-    			if ($result < 0) {
-    				$error++;
-    				$errorforline++;
-    				setEventMessages($bookkeeping->error, $bookkeeping->errors, 'errors');
+    			if ($mt) {
+        		    $bookkeeping = new BookKeeping($db);
+        			$bookkeeping->doc_date = $val["date"];
+        			$bookkeeping->doc_ref = $val["ref"];
+        			$bookkeeping->doc_type = 'bank';
+        			$bookkeeping->fk_doc = $key;
+        			$bookkeeping->fk_docdet = $val["fk_bank"];
+        			$bookkeeping->label_compte = $tabcompany[$key]['name'];
+        			$bookkeeping->montant = ($mt < 0 ? - $mt : $mt);
+        			$bookkeeping->sens = ($mt < 0) ? 'D' : 'C';
+        			$bookkeeping->debit = ($mt < 0 ? - $mt : 0);
+        			$bookkeeping->credit = ($mt >= 0) ? $mt : 0;
+        			$bookkeeping->code_journal = $journal;
+        			$bookkeeping->fk_user_author = $user->id;
+        			$bookkeeping->date_create = $now;
+        
+        			if (in_array($tabtype[$key], array('sc', 'payment_sc'))) {   // If payment is payment of social contribution
+        				$bookkeeping->code_tiers = '';
+        				$bookkeeping->numero_compte = $k;
+        			} else if ($tabtype[$key] == 'payment') {    // If payment is payment of customer invoice, we get ref of invoice
+        				$sqlmid = 'SELECT fac.facnumber';
+        				$sqlmid .= " FROM " . MAIN_DB_PREFIX . "facture fac ";
+        				$sqlmid .= " INNER JOIN " . MAIN_DB_PREFIX . "paiement_facture as payfac ON  payfac.fk_facture=fac.rowid";
+        				$sqlmid .= " INNER JOIN " . MAIN_DB_PREFIX . "paiement as pay ON  payfac.fk_paiement=pay.rowid";
+        				$sqlmid .= " WHERE pay.fk_bank=" . $key;
+        				dol_syslog("accountancy/journal/bankjournal.php:: sqlmid=" . $sqlmid, LOG_DEBUG);
+        				$resultmid = $db->query($sqlmid);
+        				if ($resultmid) {
+        					$objmid = $db->fetch_object($resultmid);
+        					$bookkeeping->doc_ref = $objmid->facnumber;
+        				}
+        				$bookkeeping->code_tiers = $tabcompany[$key]['code_compta'];
+        				$bookkeeping->numero_compte = $k;
+        			} else if ($tabtype[$key] == 'payment_supplier') {           // If payment is payment of supplier invoice, we get ref of invoice
+        
+        				$sqlmid = 'SELECT facf.ref_supplier,facf.ref';
+        				$sqlmid .= " FROM " . MAIN_DB_PREFIX . "facture_fourn facf ";
+        				$sqlmid .= " INNER JOIN " . MAIN_DB_PREFIX . "paiementfourn_facturefourn as payfacf ON  payfacf.fk_facturefourn=facf.rowid";
+        				$sqlmid .= " INNER JOIN " . MAIN_DB_PREFIX . "paiementfourn as payf ON  payfacf.fk_paiementfourn=payf.rowid";
+        				$sqlmid .= " WHERE payf.fk_bank=" . $key;
+        				dol_syslog("accountancy/journal/bankjournal.php:: sqlmid=" . $sqlmid, LOG_DEBUG);
+        				$resultmid = $db->query($sqlmid);
+        				if ($resultmid) {
+        					$objmid = $db->fetch_object($resultmid);
+        					$bookkeeping->doc_ref = $objmid->ref_supplier . ' (' . $objmid->ref . ')';
+        				}
+                        $bookkeeping->code_tiers = $tabcompany[$key]['code_compta'];
+        				$bookkeeping->numero_compte = $k;
+        			} else {
+        			    // FIXME Should be a temporary account ???
+        				$bookkeeping->doc_ref = $k;
+        				//$bookkeeping->numero_compte = $conf->global->ACCOUNTING_ACCOUNT_CUSTOMER;
+        				$bookkeeping->numero_compte = 'CodeNotDef';
+        			}
+        
+        			$result = $bookkeeping->create($user);
+        			if ($result < 0) {
+        				if ($bookkeeping->error == 'BookkeepingRecordAlreadyExists')	// Already exists
+        				{
+        					$error++;
+        					$errorforline++;
+        					//setEventMessages('Transaction for ('.$bookkeeping->doc_type.', '.$bookkeeping->doc_ref.', '.$bookkeeping->fk_docdet.') were already recorded', null, 'warnings');
+        				}
+        				else
+        				{
+    	    				$error++;
+    	    				$errorforline++;
+    	    				setEventMessages($bookkeeping->error, $bookkeeping->errors, 'errors');
+        				}
+        			}
     			}
     		}
 		}
@@ -512,6 +525,7 @@ if (! $error && $action == 'writebookkeeping') {
 }
 
 // Export
+/*
 if ($action == 'export_csv') {
 	$sep = $conf->global->ACCOUNTING_EXPORT_SEPARATORCSV;
 
@@ -678,7 +692,7 @@ if ($action == 'export_csv') {
 		}
 	}
 }
-
+*/
 
 
 
@@ -702,18 +716,19 @@ if (empty($action) || $action == 'view') {
 	$period = $form->select_date($date_start, 'date_start', 0, 0, 0, '', 1, 0, 1) . ' - ' . $form->select_date($date_end, 'date_end', 0, 0, 0, '', 1, 0, 1);
 
 	$varlink = 'id_account=' . $id_bank_account;
-	report_header($nom, $nomlink, $period, $periodlink, $description, $builddate, $exportlink, array (
-			'action' => ''
-	), '', $varlink);
+	
+	journalHead($nom, $nomlink, $period, $periodlink, $description, $builddate, $exportlink, array('action' => ''), '', $varlink);
 
-	if ($conf->global->ACCOUNTING_EXPORT_MODELCSV != 1 && $conf->global->ACCOUNTING_EXPORT_MODELCSV != 2) {
+	/*if ($conf->global->ACCOUNTING_EXPORT_MODELCSV != 1 && $conf->global->ACCOUNTING_EXPORT_MODELCSV != 2) {
 		print '<input type="button" class="butActionRefused" style="float: right;" value="' . $langs->trans('Export') . '" disabled="disabled" title="' . $langs->trans('ExportNotSupported') . '"/>';
 	} else {
 		print '<input type="button" class="butAction" style="float: right;" value="' . $langs->trans("Export") . '" onclick="launch_export();" />';
-	}
+	}*/
 
+    print '<div class="tabsAction">';
 	print '<input type="button" class="butAction" value="' . $langs->trans("WriteBookKeeping") . '" onclick="writebookkeeping();" />';
-
+    print '</div>';
+    
 	// TODO Avoid using js. We can use a direct link with $param
 	print '
 	<script type="text/javascript">

+ 146 - 104
htdocs/accountancy/journal/expensereportsjournal.php

@@ -157,100 +157,140 @@ if ($action == 'writebookkeeping') {
 	{
 		$errorforline = 0;
 
-		foreach ( $tabttc[$key] as $k => $mt ) {
-			// get compte id and label
-
-			$bookkeeping = new BookKeeping($db);
-			$bookkeeping->doc_date = $val["date"];
-			$bookkeeping->doc_ref = $val["ref"];
-			$bookkeeping->date_create = $now;
-			$bookkeeping->doc_type = 'expense_report';
-			$bookkeeping->fk_doc = $key;
-			$bookkeeping->fk_docdet = $val["fk_expensereportdet"];
-			$bookkeeping->code_tiers = $tabuser[$key]['user_accountancy_code'];
-			$bookkeeping->label_compte = $tabuser[$key]['name'];
-			$bookkeeping->numero_compte = $conf->global->SALARIES_ACCOUNTING_ACCOUNT_PAYMENT;
-			$bookkeeping->montant = $mt;
-			$bookkeeping->sens = ($mt >= 0) ? 'C' : 'D';
-			$bookkeeping->debit = ($mt <= 0) ? $mt : 0;
-			$bookkeeping->credit = ($mt > 0) ? $mt : 0;
-			$bookkeeping->code_journal = $conf->global->ACCOUNTING_EXPENSEREPORT_JOURNAL;
-			$bookkeeping->fk_user_author = $user->id;
-
-			$result = $bookkeeping->create($user);
-			if ($result < 0) {
-				$error++;
-				$errorforline++;
-				setEventMessages($bookkeeping->error, $bookkeeping->errors, 'errors');
-			}
-		}
-
-		// Fees
-		foreach ( $tabht[$key] as $k => $mt ) {
-			$accountingaccount = new AccountingAccount($db);
-			$accountingaccount->fetch(null, $k, true);
-			if ($mt) {
-				// get compte id and label
-				$accountingaccount = new AccountingAccount($db);
-				if ($accountingaccount->fetch(null, $k, true)) {
-					$bookkeeping = new BookKeeping($db);
-					$bookkeeping->doc_date = $val["date"];
-					$bookkeeping->doc_ref = $val["ref"];
-					$bookkeeping->date_create = $now;
-					$bookkeeping->doc_type = 'expense_report';
-					$bookkeeping->fk_doc = $key;
-					$bookkeeping->fk_docdet = $val["fk_expensereportdet"];
-					$bookkeeping->code_tiers = '';
-					$bookkeeping->label_compte = $accountingaccount->label;
-					$bookkeeping->numero_compte = $k;
-					$bookkeeping->montant = $mt;
-					$bookkeeping->sens = ($mt < 0) ? 'C' : 'D';
-					$bookkeeping->debit = ($mt > 0) ? $mt : 0;
-					$bookkeeping->credit = ($mt <= 0) ? $mt : 0;
-					$bookkeeping->code_journal = $conf->global->ACCOUNTING_EXPENSEREPORT_JOURNAL;
-					$bookkeeping->fk_user_author = $user->id;
-
-					$result = $bookkeeping->create($user);
-					if ($result < 0) {
-						$error++;
-						$errorforline++;
-						setEventMessages($bookkeeping->error, $bookkeeping->errors, 'errors');
-					}
-				}
-			}
-		}
-
-		// VAT
-		// var_dump($tabtva);
-		foreach ( $tabtva[$key] as $k => $mt ) {
-			if ($mt) {
-				// get compte id and label
-				$bookkeeping = new BookKeeping($db);
-				$bookkeeping->doc_date = $val["date"];
-				$bookkeeping->doc_ref = $val["ref"];
-				$bookkeeping->date_create = $now;
-				$bookkeeping->doc_type = 'expense_report';
-				$bookkeeping->fk_doc = $key;
-				$bookkeeping->fk_docdet = $val["fk_expensereportdet"];
-				$bookkeeping->code_tiers = '';
-				$bookkeeping->label_compte = $langs->trans("VAT"). ' '.$def_tva[$key];
-				$bookkeeping->numero_compte = $k;
-				$bookkeeping->montant = $mt;
-				$bookkeeping->sens = ($mt < 0) ? 'C' : 'D';
-				$bookkeeping->debit = ($mt > 0) ? $mt : 0;
-				$bookkeeping->credit = ($mt <= 0) ? $mt : 0;
-				$bookkeeping->code_journal = $conf->global->ACCOUNTING_EXPENSEREPORT_JOURNAL;
-				$bookkeeping->fk_user_author = $user->id;
-
-				$result = $bookkeeping->create($user);
-				if ($result < 0) {
-					$error++;
-					$errorforline++;
-					setEventMessages($object->error, $object->errors, 'errors');
-				}
-			}
-		}
-
+	    $db->begin();
+	     
+        if (! $errorforline)
+        {
+    	    foreach ( $tabttc[$key] as $k => $mt ) {
+    			if ($mt) {
+        	        // get compte id and label
+        
+        			$bookkeeping = new BookKeeping($db);
+        			$bookkeeping->doc_date = $val["date"];
+        			$bookkeeping->doc_ref = $val["ref"];
+        			$bookkeeping->date_create = $now;
+        			$bookkeeping->doc_type = 'expense_report';
+        			$bookkeeping->fk_doc = $key;
+        			$bookkeeping->fk_docdet = $val["fk_expensereportdet"];
+        			$bookkeeping->code_tiers = $tabuser[$key]['user_accountancy_code'];
+        			$bookkeeping->label_compte = $tabuser[$key]['name'];
+        			$bookkeeping->numero_compte = $conf->global->SALARIES_ACCOUNTING_ACCOUNT_PAYMENT;
+        			$bookkeeping->montant = $mt;
+        			$bookkeeping->sens = ($mt >= 0) ? 'C' : 'D';
+        			$bookkeeping->debit = ($mt <= 0) ? $mt : 0;
+        			$bookkeeping->credit = ($mt > 0) ? $mt : 0;
+        			$bookkeeping->code_journal = $conf->global->ACCOUNTING_EXPENSEREPORT_JOURNAL;
+        			$bookkeeping->fk_user_author = $user->id;
+        
+        			$result = $bookkeeping->create($user);
+        			if ($result < 0) {
+        			    if ($bookkeeping->error == 'BookkeepingRecordAlreadyExists')	// Already exists
+        			    {
+        			        $error++;
+        			        $errorforline++;
+        			        //setEventMessages('Transaction for ('.$bookkeeping->doc_type.', '.$bookkeeping->doc_ref.', '.$bookkeeping->fk_docdet.') were already recorded', null, 'warnings');
+        			    }
+        			    else
+        			    {
+        			        $error++;
+        			        $errorforline++;
+        			        setEventMessages($bookkeeping->error, $bookkeeping->errors, 'errors');
+        			    }
+        			}
+    			}
+    		}
+        }
+        
+        if (! $errorforline)
+        {
+            // Fees
+    		foreach ( $tabht[$key] as $k => $mt ) {
+    			$accountingaccount = new AccountingAccount($db);
+    			$accountingaccount->fetch(null, $k, true);
+    			if ($mt) {
+    				// get compte id and label
+    				$accountingaccount = new AccountingAccount($db);
+    				if ($accountingaccount->fetch(null, $k, true)) {
+    					$bookkeeping = new BookKeeping($db);
+    					$bookkeeping->doc_date = $val["date"];
+    					$bookkeeping->doc_ref = $val["ref"];
+    					$bookkeeping->date_create = $now;
+    					$bookkeeping->doc_type = 'expense_report';
+    					$bookkeeping->fk_doc = $key;
+    					$bookkeeping->fk_docdet = $val["fk_expensereportdet"];
+    					$bookkeeping->code_tiers = '';
+    					$bookkeeping->label_compte = $accountingaccount->label;
+    					$bookkeeping->numero_compte = $k;
+    					$bookkeeping->montant = $mt;
+    					$bookkeeping->sens = ($mt < 0) ? 'C' : 'D';
+    					$bookkeeping->debit = ($mt > 0) ? $mt : 0;
+    					$bookkeeping->credit = ($mt <= 0) ? $mt : 0;
+    					$bookkeeping->code_journal = $conf->global->ACCOUNTING_EXPENSEREPORT_JOURNAL;
+    					$bookkeeping->fk_user_author = $user->id;
+    
+    					$result = $bookkeeping->create($user);
+	        			if ($result < 0) {
+            			    if ($bookkeeping->error == 'BookkeepingRecordAlreadyExists')	// Already exists
+            			    {
+            			        $error++;
+            			        $errorforline++;
+            			        //setEventMessages('Transaction for ('.$bookkeeping->doc_type.', '.$bookkeeping->doc_ref.', '.$bookkeeping->fk_docdet.') were already recorded', null, 'warnings');
+            			    }
+            			    else
+            			    {
+            			        $error++;
+            			        $errorforline++;
+            			        setEventMessages($bookkeeping->error, $bookkeeping->errors, 'errors');
+            			    }
+	        			}
+    				}
+    			}
+    		}
+        }
+        
+        if (! $errorforline)
+        {
+            // VAT
+    		// var_dump($tabtva);
+    		foreach ( $tabtva[$key] as $k => $mt ) {
+    			if ($mt) {
+    				// get compte id and label
+    				$bookkeeping = new BookKeeping($db);
+    				$bookkeeping->doc_date = $val["date"];
+    				$bookkeeping->doc_ref = $val["ref"];
+    				$bookkeeping->date_create = $now;
+    				$bookkeeping->doc_type = 'expense_report';
+    				$bookkeeping->fk_doc = $key;
+    				$bookkeeping->fk_docdet = $val["fk_expensereportdet"];
+    				$bookkeeping->code_tiers = '';
+    				$bookkeeping->label_compte = $langs->trans("VAT"). ' '.$def_tva[$key];
+    				$bookkeeping->numero_compte = $k;
+    				$bookkeeping->montant = $mt;
+    				$bookkeeping->sens = ($mt < 0) ? 'C' : 'D';
+    				$bookkeeping->debit = ($mt > 0) ? $mt : 0;
+    				$bookkeeping->credit = ($mt <= 0) ? $mt : 0;
+    				$bookkeeping->code_journal = $conf->global->ACCOUNTING_EXPENSEREPORT_JOURNAL;
+    				$bookkeeping->fk_user_author = $user->id;
+    
+    				$result = $bookkeeping->create($user);
+    				if ($result < 0) {
+           			    if ($bookkeeping->error == 'BookkeepingRecordAlreadyExists')	// Already exists
+        			    {
+        			        $error++;
+        			        $errorforline++;
+        			        //setEventMessages('Transaction for ('.$bookkeeping->doc_type.', '.$bookkeeping->doc_ref.', '.$bookkeeping->fk_docdet.') were already recorded', null, 'warnings');
+        			    }
+        			    else
+        			    {
+        			        $error++;
+        			        $errorforline++;
+        			        setEventMessages($bookkeeping->error, $bookkeeping->errors, 'errors');
+        			    }
+    				}
+    			}
+    		}
+        }
+        
 		if (! $errorforline)
 		{
 		    $db->commit();
@@ -286,7 +326,7 @@ $form = new Form($db);
 $userstatic = new User($db);
 
 // Export
-if ($action == 'export_csv') {
+/*if ($action == 'export_csv') {
 	$sep = $conf->global->ACCOUNTING_EXPORT_SEPARATORCSV;
 	$journal = $conf->global->ACCOUNTING_EXPENSEREPORT_JOURNAL;
 
@@ -393,6 +433,7 @@ if ($action == 'export_csv') {
 		}
 	}
 }
+*/
 
 if (empty($action) || $action == 'view') {
 
@@ -406,18 +447,19 @@ if (empty($action) || $action == 'view') {
 	$description.= $langs->trans("DescJournalOnlyBindedVisible").'<br>';
 
 	$period = $form->select_date($date_start, 'date_start', 0, 0, 0, '', 1, 0, 1) . ' - ' . $form->select_date($date_end, 'date_end', 0, 0, 0, '', 1, 0, 1);
-	report_header($nom, $nomlink, $period, $periodlink, $description, $builddate, $exportlink, array (
-			'action' => ''
-	));
 
-	if ($conf->global->ACCOUNTING_EXPORT_MODELCSV != 1 && $conf->global->ACCOUNTING_EXPORT_MODELCSV != 2) {
+	journalHead($nom, $nomlink, $period, $periodlink, $description, $builddate, $exportlink, array('action' => ''));
+
+	/*if ($conf->global->ACCOUNTING_EXPORT_MODELCSV != 1 && $conf->global->ACCOUNTING_EXPORT_MODELCSV != 2) {
 		print '<input type="button" class="butActionRefused" style="float: right;" value="' . $langs->trans("Export") . '" disabled="disabled" title="' . $langs->trans('ExportNotSupported') . '"/>';
 	} else {
 		print '<input type="button" class="butAction" style="float: right;" value="' . $langs->trans("Export") . '" onclick="launch_export();" />';
-	}
+	}*/
 
+    print '<div class="tabsAction">';
 	print '<input type="button" class="butAction" value="' . $langs->trans("WriteBookKeeping") . '" onclick="writebookkeeping();" />';
-
+    print '</div>';
+    
 	print '
 	<script type="text/javascript">
 		function launch_export() {
@@ -511,11 +553,11 @@ if (empty($action) || $action == 'view') {
 				print "</tr>";
 			}
 		}
-		print '<tr class="oddeven">';
 
 		// Third party
 		foreach ( $tabttc[$key] as $k => $mt ) {
-			print "<td><!-- Thirdparty --></td>";
+		    print '<tr class="oddeven">';
+		    print "<td><!-- Thirdparty --></td>";
 		    print "<td>" . $date . "</td>";
 			print "<td>" . $expensereportstatic->getNomUrl(1) . "</td>";
 			$userstatic->id = $tabuser[$key]['id'];
@@ -531,8 +573,8 @@ if (empty($action) || $action == 'view') {
 			print "<td>" . $userstatic->getNomUrl(0, 'user', 16) . ' - ' . $langs->trans("Code_tiers") . "</td>";
 			print '<td align="right">' . ($mt < 0 ? - price(- $mt) : '') . "</td>";
 			print '<td align="right">' . ($mt >= 0 ? price($mt) : '') . "</td>";
+		    print "</tr>";
 		}
-		print "</tr>";
 	}
 
 	print "</table>";

+ 153 - 106
htdocs/accountancy/journal/purchasesjournal.php

@@ -109,6 +109,7 @@ dol_syslog('accountancy/journal/purchasesjournal.php:: $sql=' . $sql);
 $result = $db->query($sql);
 if ($result) {
 	$num = $db->num_rows($result);
+
 	// les variables
 	$cptfour = (! empty($conf->global->ACCOUNTING_ACCOUNT_SUPPLIER)) ? $conf->global->ACCOUNTING_ACCOUNT_SUPPLIER : $langs->trans("CodeNotDef");
 	$cpttva = (! empty($conf->global->ACCOUNTING_VAT_BUY_ACCOUNT)) ? $conf->global->ACCOUNTING_VAT_BUY_ACCOUNT : $langs->trans("CodeNotDef");
@@ -123,8 +124,10 @@ if ($result) {
 	$i = 0;
 	while ( $i < $num ) {
 		$obj = $db->fetch_object($result);
+
 		// contrôles
 		$compta_soc = (! empty($obj->code_compta_fournisseur)) ? $obj->code_compta_fournisseur : $cptfour;
+		
 		$compta_prod = $obj->compte;
 		if (empty($compta_prod)) {
 			if ($obj->product_type == 0)
@@ -144,14 +147,21 @@ if ($result) {
 
 		$tabfac[$obj->rowid]["type"] = $obj->type;
 		$tabfac[$obj->rowid]["description"] = $obj->description;
-		$tabfac[$obj->rowid]["fk_facturefourndet"] = $obj->fdid;
+		//$tabfac[$obj->rowid]["fk_facturefourndet"] = $obj->fdid;
+		
+        // Avoid warnings
+        if (! isset($tabttc[$obj->rowid][$compta_soc])) $tabttc[$obj->rowid][$compta_soc] = 0;
+        if (! isset($tabht[$obj->rowid][$compta_prod])) $tabht[$obj->rowid][$compta_prod] = 0;
+        if (! isset($tabtva[$obj->rowid][$compta_tva])) $tabtva[$obj->rowid][$compta_tva] = 0;
+
 		$tabttc[$obj->rowid][$compta_soc] += $obj->total_ttc;
 		$tabht[$obj->rowid][$compta_prod] += $obj->total_ht;
 		$tabtva[$obj->rowid][$compta_tva] += $obj->total_tva;
 		$tabcompany[$obj->rowid] = array (
 				'id' => $obj->socid,
 				'name' => $obj->name,
-				'code_fournisseur' => $obj->code_compta_fournisseur
+				'code_fournisseur' => $obj->code_fournisseur,
+				'code_compta_fournisseur' => $compta_soc
 		);
 
 		$i ++;
@@ -165,7 +175,7 @@ if ($action == 'writebookkeeping') {
 	$now = dol_now();
 	$error = 0;
 
-	foreach ($tabfac as $key => $val)
+	foreach ($tabfac as $key => $val)  // Loop on each invoice
 	{
 	    $errorforline = 0;
 	     
@@ -188,101 +198,136 @@ if ($action == 'writebookkeeping') {
 		$companystatic->code_fournisseur = $tabcompany[$key]['code_fournisseur'];
 		$companystatic->client = $tabcompany[$key]['code_client'];
 
-		foreach ( $tabttc[$key] as $k => $mt ) {
-			// get compte id and label
-
-			$bookkeeping = new BookKeeping($db);
-			$bookkeeping->doc_date = $val["date"];
-			$bookkeeping->doc_ref = $val["ref"];
-			$bookkeeping->date_create = $now;
-			$bookkeeping->doc_type = 'supplier_invoice';
-			$bookkeeping->fk_doc = $key;
-			$bookkeeping->fk_docdet = $val["fk_facturefourndet"];
-			$bookkeeping->code_tiers = $tabcompany[$key]['code_fournisseur'];
-			$bookkeeping->label_compte = dol_trunc($companystatic->name, 16) . ' - ' . $invoicestatic->refsupplier . ' - ' . $langs->trans("Code_tiers");
-			$bookkeeping->numero_compte = $conf->global->ACCOUNTING_ACCOUNT_SUPPLIER;
-			$bookkeeping->montant = $mt;
-			$bookkeeping->sens = ($mt >= 0) ? 'C' : 'D';
-			$bookkeeping->debit = ($mt <= 0) ? $mt : 0;
-			$bookkeeping->credit = ($mt > 0) ? $mt : 0;
-			$bookkeeping->code_journal = $conf->global->ACCOUNTING_PURCHASE_JOURNAL;
-			$bookkeeping->fk_user_author = $user->id;
-
-			$result = $bookkeeping->create($user);
-			if ($result < 0) {
-			    $error++;
-			    $errorforline++;
-			    setEventMessages($bookkeeping->error, $bookkeeping->errors, 'errors');
-			}
-		}
-
+        if (! $errorforline)
+        {
+    		foreach ( $tabttc[$key] as $k => $mt ) {
+    			// get compte id and label
+    		    if ($mt) {
+        			$bookkeeping = new BookKeeping($db);
+        			$bookkeeping->doc_date = $val["date"];
+        			$bookkeeping->doc_ref = $val["ref"];
+        			$bookkeeping->date_create = $now;
+        			$bookkeeping->doc_type = 'supplier_invoice';
+        			$bookkeeping->fk_doc = $key;
+        			$bookkeeping->fk_docdet = 0;    // Useless, can be several lines that are source of this record to add
+        			$bookkeeping->code_tiers = $tabcompany[$key]['code_fournisseur'];
+        			$bookkeeping->label_compte = dol_trunc($companystatic->name, 16) . ' - ' . $invoicestatic->refsupplier . ' - ' . $langs->trans("Code_tiers");
+        			$bookkeeping->numero_compte = $tabcompany[$key]['code_compta_fournisseur'];
+        			$bookkeeping->montant = $mt;
+        			$bookkeeping->sens = ($mt >= 0) ? 'C' : 'D';
+        			$bookkeeping->debit = ($mt <= 0) ? $mt : 0;
+        			$bookkeeping->credit = ($mt > 0) ? $mt : 0;
+        			$bookkeeping->code_journal = $conf->global->ACCOUNTING_PURCHASE_JOURNAL;
+        			$bookkeeping->fk_user_author = $user->id;
+        
+        			$result = $bookkeeping->create($user);
+           			if ($result < 0) {
+                        if ($bookkeeping->error == 'BookkeepingRecordAlreadyExists')	// Already exists
+                        {
+                            $error++;
+                            $errorforline++;
+                            //setEventMessages('Transaction for ('.$bookkeeping->doc_type.', '.$bookkeeping->doc_ref.', '.$bookkeeping->fk_docdet.') were already recorded', null, 'warnings');
+                        }
+                        else
+                        {
+                            $error++;
+                            $errorforline++;
+                            setEventMessages($bookkeeping->error, $bookkeeping->errors, 'errors');
+                        }
+        			}
+    		    }
+    		}
+        }
+        
 		// Product / Service
-		foreach ( $tabht[$key] as $k => $mt ) {
-			$accountingaccount = new AccountingAccount($db);
-			$accountingaccount->fetch(null, $k, true);
-			if ($mt) {
-				// get compte id and label
-				$accountingaccount = new AccountingAccount($db);
-				if ($accountingaccount->fetch(null, $k, true)) {
-					$bookkeeping = new BookKeeping($db);
-					$bookkeeping->doc_date = $val["date"];
-					$bookkeeping->doc_ref = $val["ref"];
-					$bookkeeping->date_create = $now;
-					$bookkeeping->doc_type = 'supplier_invoice';
-					$bookkeeping->fk_doc = $key;
-					$bookkeeping->fk_docdet = $val["fk_facturefourndet"];
-					$bookkeeping->code_tiers = '';
-					$bookkeeping->label_compte = dol_trunc($companystatic->name, 16) . ' - ' . $invoicestatic->refsupplier . ' - ' . $accountingaccount->label;
-					$bookkeeping->numero_compte = $k;
-					$bookkeeping->montant = $mt;
-					$bookkeeping->sens = ($mt < 0) ? 'C' : 'D';
-					$bookkeeping->debit = ($mt > 0) ? $mt : 0;
-					$bookkeeping->credit = ($mt <= 0) ? $mt : 0;
-					$bookkeeping->code_journal = $conf->global->ACCOUNTING_PURCHASE_JOURNAL;
-					$bookkeeping->fk_user_author = $user->id;
-
-					$result = $bookkeeping->create($user);
-					if ($result < 0) {
-						$error++;
-						$errorforline++;
-						setEventMessages($bookkeeping->error, $bookkeeping->errors, 'errors');
-					}
-				}
-			}
-		}
-
+        if (! $errorforline)
+        {
+            foreach ( $tabht[$key] as $k => $mt ) {
+    			$accountingaccount = new AccountingAccount($db);
+    			$accountingaccount->fetch(null, $k, true);
+    			if ($mt) {
+    				// get compte id and label
+    				$accountingaccount = new AccountingAccount($db);
+    				if ($accountingaccount->fetch(null, $k, true)) {
+    					$bookkeeping = new BookKeeping($db);
+    					$bookkeeping->doc_date = $val["date"];
+    					$bookkeeping->doc_ref = $val["ref"];
+    					$bookkeeping->date_create = $now;
+    					$bookkeeping->doc_type = 'supplier_invoice';
+    					$bookkeeping->fk_doc = $key;
+    					$bookkeeping->fk_docdet = 0;    // Useless, can be several lines that are source of this record to add
+    					$bookkeeping->code_tiers = '';
+    					$bookkeeping->label_compte = dol_trunc($companystatic->name, 16) . ' - ' . $invoicestatic->refsupplier . ' - ' . $accountingaccount->label;
+    					$bookkeeping->numero_compte = $k;
+    					$bookkeeping->montant = $mt;
+    					$bookkeeping->sens = ($mt < 0) ? 'C' : 'D';
+    					$bookkeeping->debit = ($mt > 0) ? $mt : 0;
+    					$bookkeeping->credit = ($mt <= 0) ? $mt : 0;
+    					$bookkeeping->code_journal = $conf->global->ACCOUNTING_PURCHASE_JOURNAL;
+    					$bookkeeping->fk_user_author = $user->id;
+    
+    					$result = $bookkeeping->create($user);
+    					if ($result < 0) {
+    					    if ($bookkeeping->error == 'BookkeepingRecordAlreadyExists')	// Already exists
+            			    {
+            			        $error++;
+            			        $errorforline++;
+            			        //setEventMessages('Transaction for ('.$bookkeeping->doc_type.', '.$bookkeeping->doc_ref.', '.$bookkeeping->fk_docdet.') were already recorded', null, 'warnings');
+            			    }
+            			    else
+            			    {
+            			        $error++;
+            			        $errorforline++;
+            			        setEventMessages($bookkeeping->error, $bookkeeping->errors, 'errors');
+            			    }
+    					}
+    				}
+    			}
+    		}
+        }
+        
 		// VAT
 		// var_dump($tabtva);
-		foreach ( $tabtva[$key] as $k => $mt ) {
-			if ($mt) {
-				// get compte id and label
-				$bookkeeping = new BookKeeping($db);
-				$bookkeeping->doc_date = $val["date"];
-				$bookkeeping->doc_ref = $val["ref"];
-				$bookkeeping->date_create = $now;
-				$bookkeeping->doc_type = 'supplier_invoice';
-				$bookkeeping->fk_doc = $key;
-				$bookkeeping->fk_docdet = $val["fk_facturefourndet"];
-				$bookkeeping->code_tiers = '';
-				$bookkeeping->label_compte = dol_trunc($companystatic->name, 16) . ' - ' . $invoicestatic->refsupplier . ' - ' . $langs->trans("VAT"). ' '.$def_tva[$key];
-				$bookkeeping->numero_compte = $k;
-				$bookkeeping->montant = $mt;
-				$bookkeeping->sens = ($mt < 0) ? 'C' : 'D';
-				$bookkeeping->debit = ($mt > 0) ? $mt : 0;
-				$bookkeeping->credit = ($mt <= 0) ? $mt : 0;
-				$bookkeeping->code_journal = $conf->global->ACCOUNTING_PURCHASE_JOURNAL;
-				$bookkeeping->fk_user_author = $user->id;
-
-				$result = $bookkeeping->create($user);
-				if ($result < 0) {
-					$error++;
-					$errorforline++;
-					setEventMessages($object->error, $object->errors, 'errors');
-				}
-			}
-		}
-		
-
+        if (! $errorforline)
+        {
+            foreach ( $tabtva[$key] as $k => $mt ) {
+    			if ($mt) {
+    				// get compte id and label
+    				$bookkeeping = new BookKeeping($db);
+    				$bookkeeping->doc_date = $val["date"];
+    				$bookkeeping->doc_ref = $val["ref"];
+    				$bookkeeping->date_create = $now;
+    				$bookkeeping->doc_type = 'supplier_invoice';
+    				$bookkeeping->fk_doc = $key;
+    				$bookkeeping->fk_docdet = 0;    // Useless, can be several lines that are source of this record to add
+    				$bookkeeping->code_tiers = '';
+    				$bookkeeping->label_compte = dol_trunc($companystatic->name, 16) . ' - ' . $invoicestatic->refsupplier . ' - ' . $langs->trans("VAT"). ' '.$def_tva[$key];
+    				$bookkeeping->numero_compte = $k;
+    				$bookkeeping->montant = $mt;
+    				$bookkeeping->sens = ($mt < 0) ? 'C' : 'D';
+    				$bookkeeping->debit = ($mt > 0) ? $mt : 0;
+    				$bookkeeping->credit = ($mt <= 0) ? $mt : 0;
+    				$bookkeeping->code_journal = $conf->global->ACCOUNTING_PURCHASE_JOURNAL;
+    				$bookkeeping->fk_user_author = $user->id;
+    
+    				$result = $bookkeeping->create($user);
+    				if ($result < 0) {
+    				    if ($bookkeeping->error == 'BookkeepingRecordAlreadyExists')	// Already exists
+    				    {
+    				        $error++;
+    				        $errorforline++;
+    				        //setEventMessages('Transaction for ('.$bookkeeping->doc_type.', '.$bookkeeping->doc_ref.', '.$bookkeeping->fk_docdet.') were already recorded', null, 'warnings');
+    				    }
+    				    else
+    				    {
+    				        $error++;
+    				        $errorforline++;
+    				        setEventMessages($bookkeeping->error, $bookkeeping->errors, 'errors');
+    				    }
+    				}
+    			}
+    		}
+        }
 
 		if (! $errorforline)
 		{
@@ -319,7 +364,7 @@ $form = new Form($db);
 $companystatic = new Fournisseur($db);
 
 // Export
-if ($action == 'export_csv') {
+/*if ($action == 'export_csv') {
 	$sep = $conf->global->ACCOUNTING_EXPORT_SEPARATORCSV;
 	$journal = $conf->global->ACCOUNTING_PURCHASE_JOURNAL;
 
@@ -437,6 +482,7 @@ if ($action == 'export_csv') {
 		}
 	}
 }
+*/
 
 if (empty($action) || $action == 'view') {
 
@@ -456,18 +502,19 @@ if (empty($action) || $action == 'view') {
 	}
 
 	$period = $form->select_date($date_start, 'date_start', 0, 0, 0, '', 1, 0, 1) . ' - ' . $form->select_date($date_end, 'date_end', 0, 0, 0, '', 1, 0, 1);
-	report_header($nom, $nomlink, $period, $periodlink, $description, $builddate, $exportlink, array (
-			'action' => ''
-	));
+	
+	journalHead($nom, $nomlink, $period, $periodlink, $description, $builddate, $exportlink, array('action' => ''));
 
-	if ($conf->global->ACCOUNTING_EXPORT_MODELCSV != 1 && $conf->global->ACCOUNTING_EXPORT_MODELCSV != 2) {
+	/*if ($conf->global->ACCOUNTING_EXPORT_MODELCSV != 1 && $conf->global->ACCOUNTING_EXPORT_MODELCSV != 2) {
 		print '<input type="button" class="butActionRefused" style="float: right;" value="' . $langs->trans("Export") . '" disabled="disabled" title="' . $langs->trans('ExportNotSupported') . '"/>';
 	} else {
 		print '<input type="button" class="butAction" style="float: right;" value="' . $langs->trans("Export") . '" onclick="launch_export();" />';
-	}
+	}*/
 
+    print '<div class="tabsAction">';
 	print '<input type="button" class="butAction" value="' . $langs->trans("WriteBookKeeping") . '" onclick="writebookkeeping();" />';
-
+    print '</div>';
+    
 	print '
 	<script type="text/javascript">
 		function launch_export() {
@@ -485,7 +532,7 @@ if (empty($action) || $action == 'view') {
 	/*
 	 * Show result array
 	 */
-	print '<br><br>';
+	print '<br>';
 
 	$i = 0;
 	print "<table class=\"noborder\" width=\"100%\">";
@@ -562,11 +609,11 @@ if (empty($action) || $action == 'view') {
 				print "</tr>";
 			}
 		}
-		print '<tr class="oddeven">';
 
 		// Third party
 		foreach ( $tabttc[$key] as $k => $mt ) {
-			print "<td><!-- Thirdparty --></td>";
+		    print '<tr class="oddeven">';
+		    print "<td><!-- Thirdparty --></td>";
 		    print "<td>" . $date . "</td>";
 			print "<td>" . $invoicestatic->getNomUrl(1) . "</td>";
 			$companystatic->id = $tabcompany[$key]['id'];
@@ -585,8 +632,8 @@ if (empty($action) || $action == 'view') {
 			// print "</td>";
 			print '<td align="right">' . ($mt < 0 ? - price(- $mt) : '') . "</td>";
 			print '<td align="right">' . ($mt >= 0 ? price($mt) : '') . "</td>";
+		    print "</tr>";
 		}
-		print "</tr>";
 	}
 
 	print "</table>";

+ 139 - 97
htdocs/accountancy/journal/sellsjournal.php

@@ -100,7 +100,7 @@ $sql .= " JOIN " . MAIN_DB_PREFIX . "societe as s ON s.rowid = f.fk_soc";
 $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "c_tva as ct ON fd.tva_tx = ct.taux AND ct.fk_pays = '" . $idpays . "'";
 $sql .= " WHERE fd.fk_code_ventilation > 0";
 $sql .= " AND f.entity IN (".getEntity('facture', 0).')';    // We don't share object for accountancy
-$sql .= " AND f.fk_statut > 0";
+$sql .= " AND f.fk_statut > 0"; // TODO Facture annulée ?
 if (! empty($conf->global->FACTURE_DEPOSITS_ARE_JUST_PAYMENTS)) {
     $sql .= " AND f.type IN (" . Facture::TYPE_STANDARD . "," . Facture::TYPE_REPLACEMENT . "," . Facture::TYPE_CREDIT_NOTE . "," . Facture::TYPE_SITUATION . ")";
 } else {
@@ -124,10 +124,12 @@ if ($result) {
     $num = $db->num_rows($result);
     $i = 0;
 
+    $cptcli = (! empty($conf->global->ACCOUNTING_ACCOUNT_CUSTOMER)) ? $conf->global->ACCOUNTING_ACCOUNT_CUSTOMER : $langs->trans("CodeNotDef");
+    
     while ( $i < $num ) {
         $obj = $db->fetch_object($result);
+        
         // les variables
-        $cptcli = (! empty($conf->global->ACCOUNTING_ACCOUNT_CUSTOMER)) ? $conf->global->ACCOUNTING_ACCOUNT_CUSTOMER : $langs->trans("CodeNotDef");
         $compta_soc = (! empty($obj->code_compta)) ? $obj->code_compta : $cptcli;
 
         $compta_prod = $obj->compte;
@@ -164,23 +166,24 @@ if ($result) {
         $tabfac[$obj->rowid]["ref"] = $obj->facnumber;
         $tabfac[$obj->rowid]["type"] = $obj->type;
         $tabfac[$obj->rowid]["description"] = $obj->label_compte;
-        $tabfac[$obj->rowid]["fk_facturedet"] = $obj->fdid;
-        if (! isset($tabttc[$obj->rowid][$compta_soc]))
-            $tabttc[$obj->rowid][$compta_soc] = 0;
-            if (! isset($tabht[$obj->rowid][$compta_prod]))
-                $tabht[$obj->rowid][$compta_prod] = 0;
-                if (! isset($tabtva[$obj->rowid][$compta_tva]))
-                    $tabtva[$obj->rowid][$compta_tva] = 0;
-                    $tabttc[$obj->rowid][$compta_soc] += $obj->total_ttc * $situation_ratio;
-                    $tabht[$obj->rowid][$compta_prod] += $obj->total_ht * $situation_ratio;
-                    $tabtva[$obj->rowid][$compta_tva] += $obj->total_tva * $situation_ratio;
-                    $tabcompany[$obj->rowid] = array (
-                        'id' => $obj->socid,
-                        'name' => $obj->name,
-                        'code_client' => $obj->code_compta
-                    );
-
-                    $i ++;
+        //$tabfac[$obj->rowid]["fk_facturedet"] = $obj->fdid;
+       
+        // Avoid warnings
+        if (! isset($tabttc[$obj->rowid][$compta_soc])) $tabttc[$obj->rowid][$compta_soc] = 0;
+        if (! isset($tabht[$obj->rowid][$compta_prod])) $tabht[$obj->rowid][$compta_prod] = 0;
+        if (! isset($tabtva[$obj->rowid][$compta_tva])) $tabtva[$obj->rowid][$compta_tva] = 0;
+
+        $tabttc[$obj->rowid][$compta_soc] += $obj->total_ttc * $situation_ratio;
+        $tabht[$obj->rowid][$compta_prod] += $obj->total_ht * $situation_ratio;
+        $tabtva[$obj->rowid][$compta_tva] += $obj->total_tva * $situation_ratio;
+        $tabcompany[$obj->rowid] = array (
+            'id' => $obj->socid,
+            'name' => $obj->name,
+            'code_client' => $obj->code_client,
+            'code_compta' => $compta_soc
+        );
+
+        $i ++;
     }
 } else {
     dol_print_error($db);
@@ -191,7 +194,7 @@ if ($action == 'writebookkeeping') {
     $now = dol_now();
     $error = 0;
 
-    foreach ( $tabfac as $key => $val ) {
+    foreach ( $tabfac as $key => $val ) {   // Loop on each invoice
          
         $errorforline = 0;
 
@@ -211,97 +214,135 @@ if ($action == 'writebookkeeping') {
         $invoicestatic->id = $key;
         $invoicestatic->ref = (string) $val["ref"];
 
-        foreach ( $tabttc[$key] as $k => $mt ) {
-            $bookkeeping = new BookKeeping($db);
-            $bookkeeping->doc_date = $val["date"];
-            $bookkeeping->doc_ref = $val["ref"];
-            $bookkeeping->date_create = $now;
-            $bookkeeping->doc_type = 'customer_invoice';
-            $bookkeeping->fk_doc = $key;
-            $bookkeeping->fk_docdet = $val["fk_facturedet"];
-            $bookkeeping->code_tiers = $tabcompany[$key]['code_client'];
-            $bookkeeping->numero_compte = $conf->global->ACCOUNTING_ACCOUNT_CUSTOMER;
-            // $bookkeeping->label_compte = $tabcompany[$key]['name'];
-            $bookkeeping->label_compte = dol_trunc($companystatic->name, 16) . ' - ' . $invoicestatic->ref . ' - ' . $langs->trans("Code_tiers");
-            $bookkeeping->montant = $mt;
-            $bookkeeping->sens = ($mt >= 0) ? 'D' : 'C';
-            $bookkeeping->debit = ($mt >= 0) ? $mt : 0;
-            $bookkeeping->credit = ($mt < 0) ? $mt : 0;
-            $bookkeeping->code_journal = $conf->global->ACCOUNTING_SELL_JOURNAL;
-            $bookkeeping->fk_user_author = $user->id;
-
-            $result = $bookkeeping->create($user);
-            if ($result < 0) {
-                $error++;
-                $errorforline++;
-                setEventMessages($bookkeeping->error, $bookkeeping->errors, 'errors');
+        // Thirdparty
+        if (! $errorforline)
+        {
+            foreach ( $tabttc[$key] as $k => $mt ) {
+                if ($mt) {
+                    $bookkeeping = new BookKeeping($db);
+                    $bookkeeping->doc_date = $val["date"];
+                    $bookkeeping->doc_ref = $val["ref"];
+                    $bookkeeping->date_create = $now;
+                    $bookkeeping->doc_type = 'customer_invoice';
+                    $bookkeeping->fk_doc = $key;
+                    $bookkeeping->fk_docdet = 0;    // Useless, can be several lines that are source of this record to add
+                    $bookkeeping->code_tiers = $tabcompany[$key]['code_client'];
+                    $bookkeeping->numero_compte = $tabcompany[$key]['code_compta'];
+                    // $bookkeeping->label_compte = $tabcompany[$key]['name'];
+                    $bookkeeping->label_compte = dol_trunc($companystatic->name, 16) . ' - ' . $invoicestatic->ref . ' - ' . $langs->trans("Code_tiers");
+                    $bookkeeping->montant = $mt;
+                    $bookkeeping->sens = ($mt >= 0) ? 'D' : 'C';
+                    $bookkeeping->debit = ($mt >= 0) ? $mt : 0;
+                    $bookkeeping->credit = ($mt < 0) ? $mt : 0;
+                    $bookkeeping->code_journal = $conf->global->ACCOUNTING_SELL_JOURNAL;
+                    $bookkeeping->fk_user_author = $user->id;
+    
+                    $result = $bookkeeping->create($user);
+           			if ($result < 0) {
+                        if ($bookkeeping->error == 'BookkeepingRecordAlreadyExists')	// Already exists
+                        {
+                            $error++;
+                            $errorforline++;
+                            //setEventMessages('Transaction for ('.$bookkeeping->doc_type.', '.$bookkeeping->doc_ref.', '.$bookkeeping->fk_docdet.') were already recorded', null, 'warnings');
+                        }
+                        else
+                        {
+                            $error++;
+                            $errorforline++;
+                            setEventMessages($bookkeeping->error, $bookkeeping->errors, 'errors');
+                        }
+        			}
+                }
             }
         }
-
+        
         // Product / Service
-        foreach ( $tabht[$key] as $k => $mt ) {
-            if ($mt) {
-                // get compte id and label
-                $accountingaccount = new AccountingAccount($db);
-                if ($accountingaccount->fetch(null, $k, true)) {
+        if (! $errorforline)
+        {
+            foreach ( $tabht[$key] as $k => $mt ) {
+                if ($mt) {
+                    // get compte id and label
+                    $accountingaccount = new AccountingAccount($db);
+                    if ($accountingaccount->fetch(null, $k, true)) {
+                        $bookkeeping = new BookKeeping($db);
+                        $bookkeeping->doc_date = $val["date"];
+                        $bookkeeping->doc_ref = $val["ref"];
+                        $bookkeeping->date_create = $now;
+                        $bookkeeping->doc_type = 'customer_invoice';
+                        $bookkeeping->fk_doc = $key;
+                        $bookkeeping->fk_docdet = 0;    // Useless, can be several lines that are source of this record to add;
+                        $bookkeeping->code_tiers = '';
+                        $bookkeeping->numero_compte = $k;
+                        $bookkeeping->label_compte = dol_trunc($companystatic->name, 16) . ' - ' . $invoicestatic->ref . ' - ' . $accountingaccount->label;
+                        $bookkeeping->montant = $mt;
+                        $bookkeeping->sens = ($mt < 0) ? 'D' : 'C';
+                        $bookkeeping->debit = ($mt < 0) ? $mt : 0;
+                        $bookkeeping->credit = ($mt >= 0) ? $mt : 0;
+                        $bookkeeping->code_journal = $conf->global->ACCOUNTING_SELL_JOURNAL;
+                        $bookkeeping->fk_user_author = $user->id;
+    
+                        $result = $bookkeeping->create($user);
+            			if ($result < 0) {
+                            if ($bookkeeping->error == 'BookkeepingRecordAlreadyExists')	// Already exists
+                            {
+                                $error++;
+                                $errorforline++;
+                                //setEventMessages('Transaction for ('.$bookkeeping->doc_type.', '.$bookkeeping->doc_ref.', '.$bookkeeping->fk_docdet.') were already recorded', null, 'warnings');
+                            }
+                            else
+                            {
+                                $error++;
+                                $errorforline++;
+                                setEventMessages($bookkeeping->error, $bookkeeping->errors, 'errors');
+                            }
+            			}                        
+                    }
+                }
+            }
+        }
+        
+        // VAT
+        // var_dump($tabtva);
+        if (! $errorforline)
+        {
+            foreach ( $tabtva[$key] as $k => $mt ) {
+                if ($mt) {
                     $bookkeeping = new BookKeeping($db);
                     $bookkeeping->doc_date = $val["date"];
                     $bookkeeping->doc_ref = $val["ref"];
                     $bookkeeping->date_create = $now;
                     $bookkeeping->doc_type = 'customer_invoice';
                     $bookkeeping->fk_doc = $key;
-                    $bookkeeping->fk_docdet = $val["fk_facturedet"];
+                    $bookkeeping->fk_docdet = 0;    // Useless, can be several lines that are source of this record to add
                     $bookkeeping->code_tiers = '';
                     $bookkeeping->numero_compte = $k;
-                    $bookkeeping->label_compte = dol_trunc($companystatic->name, 16) . ' - ' . $invoicestatic->ref . ' - ' . $accountingaccount->label;
+                    $bookkeeping->label_compte = dol_trunc($companystatic->name, 16) . ' - ' . $invoicestatic->ref . ' - ' . $langs->trans("VAT").' '.$def_tva[$key];
                     $bookkeeping->montant = $mt;
                     $bookkeeping->sens = ($mt < 0) ? 'D' : 'C';
                     $bookkeeping->debit = ($mt < 0) ? $mt : 0;
                     $bookkeeping->credit = ($mt >= 0) ? $mt : 0;
                     $bookkeeping->code_journal = $conf->global->ACCOUNTING_SELL_JOURNAL;
                     $bookkeeping->fk_user_author = $user->id;
-
+    
                     $result = $bookkeeping->create($user);
                     if ($result < 0) {
-                        $error++;
-                        $errorforline++;
-                        setEventMessages($bookkeeping->error, $bookkeeping->errors, 'errors');
+           			    if ($bookkeeping->error == 'BookkeepingRecordAlreadyExists')	// Already exists
+        			    {
+        			        $error++;
+        			        $errorforline++;
+        			        //setEventMessages('Transaction for ('.$bookkeeping->doc_type.', '.$bookkeeping->doc_ref.', '.$bookkeeping->fk_docdet.') were already recorded', null, 'warnings');
+        			    }
+        			    else
+        			    {
+        			        $error++;
+        			        $errorforline++;
+        			        setEventMessages($bookkeeping->error, $bookkeeping->errors, 'errors');
+        			    }
                     }
                 }
             }
         }
 
-        // VAT
-        // var_dump($tabtva);
-        foreach ( $tabtva[$key] as $k => $mt ) {
-            if ($mt) {
-                $bookkeeping = new BookKeeping($db);
-                $bookkeeping->doc_date = $val["date"];
-                $bookkeeping->doc_ref = $val["ref"];
-                $bookkeeping->date_create = $now;
-                $bookkeeping->doc_type = 'customer_invoice';
-                $bookkeeping->fk_doc = $key;
-                $bookkeeping->fk_docdet = $val["fk_facturedet"];
-                $bookkeeping->code_tiers = '';
-                $bookkeeping->numero_compte = $k;
-                $bookkeeping->label_compte = dol_trunc($companystatic->name, 16) . ' - ' . $invoicestatic->ref . ' - ' . $langs->trans("VAT").' '.$def_tva[$key];
-                $bookkeeping->montant = $mt;
-                $bookkeeping->sens = ($mt < 0) ? 'D' : 'C';
-                $bookkeeping->debit = ($mt < 0) ? $mt : 0;
-                $bookkeeping->credit = ($mt >= 0) ? $mt : 0;
-                $bookkeeping->code_journal = $conf->global->ACCOUNTING_SELL_JOURNAL;
-                $bookkeeping->fk_user_author = $user->id;
-
-                $result = $bookkeeping->create($user);
-                if ($result < 0) {
-                    $error++;
-                    $errorforline++;
-                    setEventMessages($object->error, $object->errors, 'errors');
-                }
-            }
-        }
-
-
         if (! $errorforline)
         {
             $db->commit();
@@ -337,7 +378,7 @@ if ($action == 'writebookkeeping') {
 $form = new Form($db);
 
 // Export
-if ($action == 'export_csv') {
+/*if ($action == 'export_csv') {
 
     $sep = $conf->global->ACCOUNTING_EXPORT_SEPARATORCSV;
     $sell_journal = $conf->global->ACCOUNTING_SELL_JOURNAL;
@@ -457,6 +498,7 @@ if ($action == 'export_csv') {
         }
     }
 }
+*/
 
 if (empty($action) || $action == 'view') {
 
@@ -474,18 +516,18 @@ if (empty($action) || $action == 'view') {
 	else
 		$description .= $langs->trans("DepositsAreIncluded");
 	$period = $form->select_date($date_start, 'date_start', 0, 0, 0, '', 1, 0, 1) . ' - ' . $form->select_date($date_end, 'date_end', 0, 0, 0, '', 1, 0, 1);
-	report_header($nom, $nomlink, $period, $periodlink, $description, $builddate, $exportlink, array (
-			'action' => ''
-	));
+	
+	journalHead($nom, $nomlink, $period, $periodlink, $description, $builddate, $exportlink, array('action' => ''));
 
-	if ($conf->global->ACCOUNTING_EXPORT_MODELCSV != 1 && $conf->global->ACCOUNTING_EXPORT_MODELCSV != 2) {
+	/*if ($conf->global->ACCOUNTING_EXPORT_MODELCSV != 1 && $conf->global->ACCOUNTING_EXPORT_MODELCSV != 2) {
 		print '<input type="button" class="butActionRefused" style="float: right;" value="' . $langs->trans("Export") . '" disabled="disabled" title="' . $langs->trans('ExportNotSupported') . '"/>';
 	} else {
 		print '<input type="button" class="butAction" style="float: right;" value="' . $langs->trans("Export") . '" onclick="launch_export();" />';
-	}
-
+	}*/
+    print '<div class="tabsAction">';
 	print '<input type="button" class="butAction" value="' . $langs->trans("WriteBookKeeping") . '" onclick="writebookkeeping();" />';
-
+    print '</div>';
+    
 	print '
 	<script type="text/javascript">
 		function launch_export() {
@@ -503,7 +545,7 @@ if (empty($action) || $action == 'view') {
 	/*
 	 * Show result array
 	 */
-	print '<br><br>';
+	print '<br>';
 
 	$i = 0;
 	print "<table class=\"noborder\" width=\"100%\">";
@@ -552,8 +594,8 @@ if (empty($action) || $action == 'view') {
 			print "<td>" . $companystatic->getNomUrl(0, 'customer', 16) . ' - ' . $invoicestatic->ref . ' - ' . $langs->trans("Code_tiers") . "</td>";
 			print "</td><td align='right'>" . ($mt >= 0 ? price($mt) : '') . "</td>";
 			print "<td align='right'>" . ($mt < 0 ? price(- $mt) : '') . "</td>";
+		    print "</tr>";
 		}
-		print "</tr>";
 
 		// Product / Service
 		foreach ( $tabht[$key] as $k => $mt ) {

+ 7 - 2
htdocs/accountancy/supplier/lines.php

@@ -44,6 +44,7 @@ $langs->load("productbatch");
 $account_parent = GETPOST('account_parent');
 $changeaccount = GETPOST('changeaccount');
 // Search Getpost
+$search_lineid = GETPOST('search_lineid', 'int');
 $search_ref = GETPOST('search_ref', 'alpha');
 $search_invoice = GETPOST('search_invoice', 'alpha');
 $search_label = GETPOST('search_label', 'alpha');
@@ -85,6 +86,7 @@ $formventilation = new FormVentilation($db);
 // Purge search criteria
 if (GETPOST("button_removefilter_x") || GETPOST("button_removefilter.x") || GETPOST("button_removefilter")) // All tests are required to be compatible with all browsers
 {
+    $search_lineid = '';
 	$search_ref = '';
 	$search_invoice = '';
 	$search_label = '';
@@ -148,7 +150,7 @@ print '<script type="text/javascript">
  * Supplier Invoice lines
  */
 $sql = "SELECT f.rowid as facid, f.ref as facnumber, f.ref_supplier, f.libelle as invoice_label, f.datef,";
-$sql.= " l.fk_product, l.description, l.total_ht , l.qty, l.rowid, l.tva_tx, aa.label, aa.account_number, ";
+$sql.= " l.rowid, l.fk_product, l.description, l.total_ht , l.qty, l.tva_tx, aa.label, aa.account_number, ";
 $sql.= " p.rowid as product_id, p.ref as product_ref, p.label as product_label, p.fk_product_type as type";
 $sql.= " FROM " . MAIN_DB_PREFIX . "facture_fourn as f";
 $sql.= " , " . MAIN_DB_PREFIX . "accounting_account as aa";
@@ -156,6 +158,9 @@ $sql.= " , " . MAIN_DB_PREFIX . "facture_fourn_det as l";
 $sql.= " LEFT JOIN " . MAIN_DB_PREFIX . "product as p ON p.rowid = l.fk_product";
 $sql.= " WHERE f.rowid = l.fk_facture_fourn and f.fk_statut >= 1 AND l.fk_code_ventilation <> 0 ";
 $sql.= " AND aa.rowid = l.fk_code_ventilation";
+if ($search_lineid) {
+    $sql .= natural_search("l.rowid", $search_lineid, 1);
+}
 if (strlen(trim($search_invoice))) {
 	$sql .= natural_search("f.ref", $search_invoice);
 }
@@ -240,7 +245,7 @@ if ($result) {
 	print '<table class="tagtable liste'.($moreforfilter?" listwithfilterbefore":"").'">'."\n";
 	
 	print '<tr class="liste_titre_filter">';
-    print '<td class="liste_titre"></td>';
+	print '<td class="liste_titre"><input type="text" class="flat maxwidth50" name="search_lineid" value="' . dol_escape_htmltag($search_lineid) . '""></td>';
 	print '<td class="liste_titre"><input type="text" class="flat maxwidth50" name="search_invoice" value="' . dol_escape_htmltag($search_invoice) . '"></td>';
 	print '<td class="liste_titre"></td>';
 	print '<td class="liste_titre"></td>';

+ 7 - 2
htdocs/accountancy/supplier/list.php

@@ -52,6 +52,7 @@ $toselect = GETPOST('toselect', 'array');
 $mesCasesCochees = GETPOST('toselect', 'array');
 
 // Search Getpost
+$search_lineid = GETPOST('search_lineid', 'int');
 $search_invoice = GETPOST('search_invoice', 'alpha');
 $search_ref = GETPOST('search_ref', 'alpha');
 $search_label = GETPOST('search_label', 'alpha');
@@ -101,6 +102,7 @@ if (! GETPOST('confirmmassaction') && $massaction != 'presend' && $massaction !=
 // 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_lineid = '';
     $search_ref = '';
     $search_invoice = '';    
     $search_label = '';
@@ -179,7 +181,7 @@ llxHeader('', $langs->trans("SuppliersVentilation"));
 
 // Supplier Invoice Lines
 $sql = "SELECT f.rowid as facid, f.ref, f.ref_supplier, f.libelle as invoice_label, f.datef,";
-$sql.= " l.fk_product, l.description, l.total_ht as price, l.rowid, l.fk_code_ventilation, l.product_type as type_l, l.tva_tx as tva_tx_line, ";
+$sql.= " l.rowid, l.fk_product, l.description, l.total_ht as price, l.fk_code_ventilation, l.product_type as type_l, l.tva_tx as tva_tx_line, ";
 $sql.= " p.rowid as product_id, p.ref as product_ref, p.label as product_label, p.fk_product_type as type, p.accountancy_code_buy as code_buy, p.tva_tx as tva_tx_prod,";
 $sql.= " aa.rowid as aarowid";
 $sql.= " FROM " . MAIN_DB_PREFIX . "facture_fourn as f";
@@ -191,6 +193,9 @@ $sql.= " WHERE f.fk_statut > 0 AND l.fk_code_ventilation <= 0";
 $sql.= " AND product_type <= 2";
 $sql.= " AND (accsys.rowid='" . $conf->global->CHARTOFACCOUNTS . "' OR p.accountancy_code_buy IS NULL OR p.accountancy_code_buy ='')";
 // Add search filter like
+if ($search_lineid) {
+    $sql .= natural_search("l.rowid", $search_lineid, 1);
+}
 if (strlen(trim($search_invoice))) {
     $sql .= natural_search("f.ref",$search_invoice);
 }
@@ -272,7 +277,7 @@ if ($result) {
 
 	// We add search filter
 	print '<tr class="liste_titre_filter">';
-	print '<td class="liste_titre"></td>';
+	print '<td class="liste_titre"><input type="text" class="flat maxwidth50" name="search_lineid" value="' . dol_escape_htmltag($search_lineid) . '""></td>';
 	print '<td class="liste_titre"><input type="text" class="flat maxwidth50" name="search_invoice" value="' . dol_escape_htmltag($search_invoice) . '"></td>';
 	print '<td class="liste_titre"></td>';
 	print '<td class="liste_titre"></td>';

+ 3 - 3
htdocs/adherents/subscription.php

@@ -89,14 +89,14 @@ if ($rowid)
     $caneditfieldmember=$user->rights->adherent->creer;
 }
 
+// Initialize technical object to manage hooks of thirdparties. Note that conf->hooks_modules contains array array
+$hookmanager->initHooks(array('subscription'));
+
 // PDF
 $hidedetails = (GETPOST('hidedetails', 'int') ? GETPOST('hidedetails', 'int') : (! empty($conf->global->MAIN_GENERATE_DOCUMENTS_HIDE_DETAILS) ? 1 : 0));
 $hidedesc = (GETPOST('hidedesc', 'int') ? GETPOST('hidedesc', 'int') : (! empty($conf->global->MAIN_GENERATE_DOCUMENTS_HIDE_DESC) ? 1 : 0));
 $hideref = (GETPOST('hideref', 'int') ? GETPOST('hideref', 'int') : (! empty($conf->global->MAIN_GENERATE_DOCUMENTS_HIDE_REF) ? 1 : 0));
 
-
-
-
 /*
  * 	Actions
  */

+ 1 - 1
htdocs/adherents/subscription/card.php

@@ -145,7 +145,7 @@ if ($action == 'confirm_delete' && $confirm == 'yes' && $user->rights->adherent-
     $result=$object->delete($user);
     if ($result > 0)
     {
-    	header("Location: card_subscriptions.php?rowid=".$object->fk_adherent);
+    	header("Location: ".DOL_URL_ROOT."/adherents/card.php?rowid=".$object->fk_adherent);
     	exit;
     }
     else

+ 1 - 1
htdocs/admin/agenda.php

@@ -144,7 +144,7 @@ print '<input type="hidden" name="action" value="save">';
 
 $head=agenda_prepare_head();
 
-dol_fiche_head($head, 'autoactions', $langs->trans("Agenda"), 0, 'action');
+dol_fiche_head($head, 'autoactions', $langs->trans("Agenda"), -1, 'action');
 
 print $langs->trans("AgendaAutoActionDesc")."<br>\n";
 print $langs->trans("OnlyActiveElementsAreShown").'<br>';

+ 1 - 1
htdocs/admin/agenda_extrafields.php

@@ -75,7 +75,7 @@ print "<br>\n";
 
 $head=agenda_prepare_head();
 
-dol_fiche_head($head, 'attributes', $langs->trans("Agenda"), 0, 'action');
+dol_fiche_head($head, 'attributes', $langs->trans("Agenda"), -1, 'action');
 
 require DOL_DOCUMENT_ROOT.'/core/tpl/admin_extrafields_view.tpl.php';
 

+ 1 - 1
htdocs/admin/agenda_extsites.php

@@ -137,7 +137,7 @@ print '<input type="hidden" name="action" value="save">';
 
 $head=agenda_prepare_head();
 
-dol_fiche_head($head, 'extsites', $langs->trans("Agenda"), 0, 'action');
+dol_fiche_head($head, 'extsites', $langs->trans("Agenda"), -1, 'action');
 
 print $langs->trans("AgendaExtSitesDesc")."<br>\n";
 print "<br>\n";

+ 1 - 1
htdocs/admin/agenda_other.php

@@ -196,7 +196,7 @@ print "<br>\n";
 
 $head=agenda_prepare_head();
 
-dol_fiche_head($head, 'other', $langs->trans("Agenda"), 0, 'action');
+dol_fiche_head($head, 'other', $langs->trans("Agenda"), -1, 'action');
 
 
 /*

+ 1 - 1
htdocs/admin/agenda_xcal.php

@@ -83,7 +83,7 @@ print '<input type="hidden" name="token" value="'.$_SESSION['newtoken'].'">';
 
 $head=agenda_prepare_head();
 
-dol_fiche_head($head, 'xcal', $langs->trans("Agenda"), 0, 'action');
+dol_fiche_head($head, 'xcal', $langs->trans("Agenda"), -1, 'action');
 
 print $langs->trans("AgendaSetupOtherDesc")."<br>\n";
 print "<br>\n";

+ 7 - 1
htdocs/admin/chequereceipts.php

@@ -257,8 +257,14 @@ $var=true;
 
 $var=! $var;
 
+$substitutionarray=pdf_getSubstitutionArray($langs);
+$substitutionarray['__(AnyTranslationKey)__']=$langs->trans("Translation");
+$htmltext = '<i>'.$langs->trans("AvailableVariables").':<br>';
+foreach($substitutionarray as $key => $val)	$htmltext.=$key.'<br>';
+$htmltext.='</i>';
+
 print '<tr class="oddeven"><td colspan="2">';
-print $langs->trans("FreeLegalTextOnChequeReceipts").' ('.$langs->trans("AddCRIfTooLong").')<br>';
+print $form->textwithpicto($langs->trans("FreeLegalTextOnChequeReceipts"), $langs->trans("AddCRIfTooLong").'<br><br>'.$htmltext).'<br>';
 $variablename='BANK_CHEQUERECEIPT_FREE_TEXT';
 if (empty($conf->global->PDF_ALLOW_HTML_FOR_FREE_TEXT))
 {

+ 2 - 1
htdocs/admin/clicktodial.php

@@ -91,7 +91,8 @@ print '</td></tr>';
 
 print '<tr class="oddeven"><td>';
 print $langs->trans("DefaultLink").'</td><td>';
-print '<input style="width: 90%" type="text" name="CLICKTODIAL_URL"'.($conf->global->CLICKTODIAL_USE_TEL_LINK_ON_PHONE_NUMBERS?' disabled="disabled"':'').' value="'.$conf->global->CLICKTODIAL_URL.'"><br>';
+print '<input class="quatrevingtpercent" type="text" id="CLICKTODIAL_URL" name="CLICKTODIAL_URL"'.($conf->global->CLICKTODIAL_USE_TEL_LINK_ON_PHONE_NUMBERS?' disabled="disabled"':'').' value="'.$conf->global->CLICKTODIAL_URL.'"><br>';
+print ajax_autoselect('CLICKTODIAL_URL');
 print '<br>';
 print $langs->trans("ClickToDialUrlDesc").'<br>';
 print $langs->trans("Example").':<br>http://myphoneserver/mypage?login=__LOGIN__&password=__PASS__&caller=__PHONEFROM__&called=__PHONETO__';

+ 7 - 6
htdocs/admin/commande.php

@@ -565,12 +565,18 @@ print "<td>&nbsp;</td>\n";
 print "</tr>\n";
 $var=true;
 
+$substitutionarray=pdf_getSubstitutionArray($langs);
+$substitutionarray['__(AnyTranslationKey)__']=$langs->trans("Translation");
+$htmltext = '<i>'.$langs->trans("AvailableVariables").':<br>';
+foreach($substitutionarray as $key => $val)	$htmltext.=$key.'<br>';
+$htmltext.='</i>';
+
 $var=! $var;
 print '<form action="'.$_SERVER["PHP_SELF"].'" method="post">';
 print '<input type="hidden" name="token" value="'.$_SESSION['newtoken'].'">';
 print '<input type="hidden" name="action" value="set_ORDER_FREE_TEXT">';
 print '<tr class="oddeven"><td colspan="2">';
-print $langs->trans("FreeLegalTextOnOrders").' '.img_info($langs->trans("AddCRIfTooLong")).'<br>';
+print $form->textwithpicto($langs->trans("FreeLegalTextOnOrders"), $langs->trans("AddCRIfTooLong").'<br><br>'.$htmltext).'<br>';
 $variablename='ORDER_FREE_TEXT';
 if (empty($conf->global->PDF_ALLOW_HTML_FOR_FREE_TEXT))
 {
@@ -593,11 +599,6 @@ print "<form method=\"post\" action=\"".$_SERVER["PHP_SELF"]."\">";
 print '<input type="hidden" name="token" value="'.$_SESSION['newtoken'].'">';
 print "<input type=\"hidden\" name=\"action\" value=\"set_COMMANDE_DRAFT_WATERMARK\">";
 print '<tr class="oddeven"><td>';
-$substitutionarray=pdf_getSubstitutionArray($langs);
-$substitutionarray['__(AnyTranslationKey)__']=$langs->trans("Translation");
-$htmltext = '<i>'.$langs->trans("AvailableVariables").':<br>';
-foreach($substitutionarray as $key => $val) $htmltext.=$key.'<br>';
-$htmltext.='</i>';
 print $form->textwithpicto($langs->trans("WatermarkOnDraftOrders"), $htmltext);
 print '</td><td>';
 print '<input class="flat minwidth200" type="text" name="COMMANDE_DRAFT_WATERMARK" value="'.$conf->global->COMMANDE_DRAFT_WATERMARK.'">';

+ 9 - 2
htdocs/admin/contract.php

@@ -502,9 +502,15 @@ print '<td align="center" width="60">'.$langs->trans("Value").'</td>';
 print "</tr>\n";
 $var=true;
 
+$substitutionarray=pdf_getSubstitutionArray($langs);
+$substitutionarray['__(AnyTranslationKey)__']=$langs->trans("Translation");
+$htmltext = '<i>'.$langs->trans("AvailableVariables").':<br>';
+foreach($substitutionarray as $key => $val)	$htmltext.=$key.'<br>';
+$htmltext.='</i>';
+
 $var=! $var;
 print '<tr class="oddeven"><td colspan="2">';
-print $langs->trans("FreeLegalTextOnContracts").' '.img_info($langs->trans("AddCRIfTooLong")).'<br>';
+print $form->textwithpicto($langs->trans("FreeLegalTextOnContracts"), $langs->trans("AddCRIfTooLong").'<br><br>'.$htmltext).'<br>';
 $variablename='CONTRACT_FREE_TEXT';
 if (empty($conf->global->PDF_ALLOW_HTML_FOR_FREE_TEXT))
 {
@@ -521,7 +527,8 @@ print '</td></tr>'."\n";
 //Use draft Watermark
 
 print '<tr class="oddeven"><td>';
-print $langs->trans("WatermarkOnDraftContractCards").'</td><td>';
+print $form->textwithpicto($langs->trans("WatermarkOnDraftContractCards"), $htmltext);
+print '</td><td>';
 print '<input size="50" class="flat" type="text" name="CONTRACT_DRAFT_WATERMARK" value="'.$conf->global->CONTRACT_DRAFT_WATERMARK.'">';
 print '</td></tr>'."\n";
 

+ 84 - 20
htdocs/admin/defaultvalues.php

@@ -56,6 +56,10 @@ $defaultvalue = GETPOST('defaultvalue');
 
 $defaulturl=preg_replace('/^\//', '', $defaulturl);
 
+$urlpage = GETPOST('urlpage');
+$key = GETPOST('key');
+$value = GETPOST('value');
+
 
 /*
  * Actions
@@ -81,25 +85,51 @@ if (GETPOST("button_removefilter_x") || GETPOST("button_removefilter.x") || GETP
 }
 
 
-if ($action == 'add' || (GETPOST('add') && $action != 'update'))
+if (($action == 'add' || (GETPOST('add') && $action != 'update')) || GETPOST('actionmodify'))
 {
 	$error=0;
 
-	if (empty($defaulturl))
+	if (($action == 'add' || (GETPOST('add') && $action != 'update')))
 	{
-		setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Url")), null, 'errors');
-		$error++;
+    	if (empty($defaulturl))
+    	{
+    		setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Url")), null, 'errors');
+    		$error++;
+    	}
+    	if (empty($defaultkey))
+    	{
+    		setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Field")), null, 'errors');
+    		$error++;
+    	}
 	}
-	if (empty($defaultkey))
+	if (GETPOST('actionmodify'))
 	{
-		setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Key")), null, 'errors');
-		$error++;
+	    if (empty($urlpage))
+	    {
+	        setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Url")), null, 'errors');
+	        $error++;
+	    }
+	    if (empty($key))
+	    {
+	        setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Field")), null, 'errors');
+	        $error++;
+	    }
 	}
+	
 	if (! $error)
 	{
 	    $db->begin();
-	     
-		$sql = "INSERT INTO ".MAIN_DB_PREFIX."default_values(type, user_id, page, param, value, entity) VALUES ('".$db->escape($mode)."', 0, '".$db->escape($defaulturl)."','".$db->escape($defaultkey)."','".$db->escape($defaultvalue)."', ".$db->escape($conf->entity).")";
+	    
+	    if ($action == 'add' || (GETPOST('add') && $action != 'update'))
+	    {
+            $sql = "INSERT INTO ".MAIN_DB_PREFIX."default_values(type, user_id, page, param, value, entity) VALUES ('".$db->escape($mode)."', 0, '".$db->escape($defaulturl)."','".$db->escape($defaultkey)."','".$db->escape($defaultvalue)."', ".$db->escape($conf->entity).")";
+	    }
+	    if (GETPOST('actionmodify'))
+	    {
+		    $sql = "UPDATE ".MAIN_DB_PREFIX."default_values SET page = '".$db->escape($urlpage)."', param = '".$db->escape($key)."', value = '".$db->escape($value)."'";
+		    $sql.= " WHERE rowid = ".$id;
+	    }
+		
 		$result = $db->query($sql);
 		if ($result > 0)
 		{
@@ -176,6 +206,10 @@ $head=defaultvalues_prepare_head();
     
 dol_fiche_head($head, $mode, '', -1, '');
 
+if ($mode == 'sortorder')
+{
+    print info_admin($langs->trans("WarningSettingSortOrder")).'<br>';
+}
 
 print '<input type="hidden" name="token" value="'.$_SESSION['newtoken'].'">';
 print '<input type="hidden" id="action" name="action" value="">';
@@ -187,29 +221,37 @@ $texthelp=$langs->trans("PageUrlForDefaultValues");
 if ($mode == 'createform') $texthelp.=$langs->trans("PageUrlForDefaultValuesCreate", 'societe/card.php');
 else $texthelp.=$langs->trans("PageUrlForDefaultValuesList", 'societe/list.php');
 $texturl=$form->textwithpicto($langs->trans("Url"), $texthelp);
-print_liste_field_titre($texturl,$_SERVER["PHP_SELF"],'defaulturl','',$param,'',$sortfield,$sortorder);
+print_liste_field_titre($texturl,$_SERVER["PHP_SELF"],'page,param','',$param,'',$sortfield,$sortorder);
 $texthelp=$langs->trans("TheKeyIsTheNameOfHtmlField");
-if ($mode != 'sortorder') $textkey=$form->textwithpicto($langs->trans("Key"), $texthelp);
-else $textkey=$form->textwithpicto($langs->trans("Key"), $texthelp);
-print_liste_field_titre($textkey,$_SERVER["PHP_SELF"],'defaultkey','',$param,'',$sortfield,$sortorder);
+if ($mode != 'sortorder') 
+{
+    $textkey=$form->textwithpicto($langs->trans("Field"), $texthelp);
+}
+else 
+{
+    $texthelp='field or alias.field';
+    $textkey=$form->textwithpicto($langs->trans("Field"), $texthelp);
+}
+print_liste_field_titre($textkey,$_SERVER["PHP_SELF"],'param','',$param,'',$sortfield,$sortorder);
 if ($mode != 'sortorder')
 {
     $texthelp=$langs->trans("FollowingConstantsWillBeSubstituted").'<br>';
     // See list into GETPOST
     $texthelp.='__USERID__<br>';
+    $texthelp.='__SUPERVISORID__<br>';
     $texthelp.='__MYCOUNTRYID__<br>';
     $texthelp.='__DAY__<br>';
     $texthelp.='__MONTH__<br>';
     $texthelp.='__YEAR__<br>';
     if (! empty($conf->multicompany->enabled)) $texthelp.='__ENTITYID__<br>';
-    $textvalue=$form->textwithpicto($langs->trans("Value"), $texthelp);
+    $textvalue=$form->textwithpicto($langs->trans("Value"), $texthelp, 1, 'help', '', 0, 2, '');
 }
 else
 {
     $texthelp='ASC or DESC';
     $textvalue=$form->textwithpicto($langs->trans("SortOrder"), $texthelp);
 }
-print_liste_field_titre($textvalue, $_SERVER["PHP_SELF"], 'defaultvalue', '', $param, '', $sortfield, $sortorder);
+print_liste_field_titre($textvalue, $_SERVER["PHP_SELF"], 'value', '', $param, '', $sortfield, $sortorder);
 if (! empty($conf->multicompany->enabled) && !$user->entity) print_liste_field_titre($langs->trans("Entity"),$_SERVER["PHP_SELF"],'entity,page','',$param,'',$sortfield,$sortorder);
 print '<td align="center"></td>';
 print "</tr>\n";
@@ -269,9 +311,17 @@ if ($result)
 
 		print '<tr class="oddeven">';
 		
-		print '<td>'.$obj->page.'</td>'."\n";
-   	    print '<td>'.$obj->param.'</td>'."\n";
-        
+		print '<td>';
+		if ($action != 'edit' || GETPOST('rowid') != $obj->rowid) print $obj->page;
+		else print '<input type="text" name="urlpage" value="'.dol_escape_htmltag($obj->page).'">';
+		print '</td>'."\n";
+   	    
+		// Key
+		print '<td>';
+		if ($action != 'edit' || GETPOST('rowid') != $obj->rowid) print $obj->param;
+		else print '<input type="text" name="key" value="'.dol_escape_htmltag($obj->param).'">';
+		print '</td>'."\n";
+
 		// Value
 		print '<td>';
 		/*print '<input type="hidden" name="const['.$i.'][rowid]" value="'.$obj->rowid.'">';
@@ -279,11 +329,25 @@ if ($result)
 		print '<input type="hidden" name="const['.$i.'][name]" value="'.$obj->transkey.'">';
 		print '<input type="text" id="value_'.$i.'" class="flat inputforupdate" size="30" name="const['.$i.'][value]" value="'.dol_escape_htmltag($obj->transvalue).'">';
 		*/
-		print $obj->value;
+		if ($action != 'edit' || GETPOST('rowid') != $obj->rowid) print $obj->value;
+		else print '<input type="text" name="value" value="'.dol_escape_htmltag($obj->value).'">';
 		print '</td>';
 
 		print '<td align="center">';
-		print '<a href="'.$_SERVER['PHP_SELF'].'?rowid='.$obj->rowid.'&entity='.$obj->entity.'&mode='.$mode.'&action=delete'.((empty($user->entity) && $debug)?'&debug=1':'').'">'.img_delete().'</a>';
+		if ($action != 'edit' || GETPOST('rowid') != $obj->rowid)
+		{
+    		print '<a href="'.$_SERVER['PHP_SELF'].'?rowid='.$obj->rowid.'&entity='.$obj->entity.'&mode='.$mode.'&action=edit'.((empty($user->entity) && $debug)?'&debug=1':'').'">'.img_edit().'</a>';
+    		print ' &nbsp; ';
+    		print '<a href="'.$_SERVER['PHP_SELF'].'?rowid='.$obj->rowid.'&entity='.$obj->entity.'&mode='.$mode.'&action=delete'.((empty($user->entity) && $debug)?'&debug=1':'').'">'.img_delete().'</a>';
+		}
+		else
+		{
+		    print '<input type="hidden" name="page" value="'.$page.'">';
+		    print '<input type="hidden" name="rowid" value="'.$id.'">';
+		    print '<div name="'.(! empty($obj->rowid)?$obj->rowid:'none').'"></div>';
+		    print '<input type="submit" class="button" name="actionmodify" value="'.$langs->trans("Modify").'">';
+		    print '<input type="submit" class="button" name="actioncancel" value="'.$langs->trans("Cancel").'">';
+		}
 		print '</td>';
 		
 		print "</tr>\n";

+ 20 - 14
htdocs/admin/dict.php

@@ -660,7 +660,11 @@ if (GETPOST('actionadd') || GETPOST('actionmodify'))
 	if ($_POST["accountancy_code"] <= 0) $_POST["accountancy_code"]='';	// If empty, we force to null
 	if ($_POST["accountancy_code_sell"] <= 0) $_POST["accountancy_code_sell"]='';	// If empty, we force to null
 	if ($_POST["accountancy_code_buy"] <= 0) $_POST["accountancy_code_buy"]='';	// If empty, we force to null
-
+    if ($id == 10 && isset($_POST["code"]))  // Spaces are not allowed into code 
+    {
+        $_POST["code"]=preg_replace('/\s/','',$_POST["code"]);
+    }
+    
     // Si verif ok et action add, on ajoute la ligne
     if ($ok && GETPOST('actionadd'))
     {
@@ -914,10 +918,19 @@ if (empty($id))
 print "<br>\n";
 
 
+$param = '&id='.$id;
+if ($search_country_id > 0) $param.= '&search_country_id='.$search_country_id;
+if ($search_code != '')     $param.= '&search_code='.urlencode($search_country_id);
+$paramwithsearch = $param;
+if ($sortorder) $paramwithsearch.= '&sortorder='.$sortorder;
+if ($sortfield) $paramwithsearch.= '&sortfield='.$sortfield;
+if (GETPOST('from')) $paramwithsearch.= '&from='.GETPOST('from','alpha');
+
+
 // Confirmation de la suppression de la ligne
 if ($action == 'delete')
 {
-    print $form->formconfirm($_SERVER["PHP_SELF"].'?'.($page?'page='.$page.'&':'').'sortfield='.$sortfield.'&sortorder='.$sortorder.'&rowid='.$rowid.'&code='.urlencode($_GET["code"]).'&id='.$id, $langs->trans('DeleteLine'), $langs->trans('ConfirmDeleteLine'), 'confirm_delete','',0,1);
+    print $form->formconfirm($_SERVER["PHP_SELF"].'?'.($page?'page='.$page.'&':'').'rowid='.$rowid.'&code='.urlencode($_GET["code"]).$paramwithsearch, $langs->trans('DeleteLine'), $langs->trans('ConfirmDeleteLine'), 'confirm_delete','',0,1);
 }
 //var_dump($elementList);
 
@@ -1069,7 +1082,7 @@ if ($id)
         print '</tr>';
 
         // Line to enter new values
-        print '<tr class="oddeven nodrag nodrop nohover">';
+        print '<!-- line to add new entry --><tr class="oddeven nodrag nodrop nohover">';
 
         $obj = new stdClass();
         // If data was already input, we define them in obj to populate input fields.
@@ -1130,14 +1143,6 @@ if ($id)
         $num = $db->num_rows($resql);
         $i = 0;
 
-        $param = '&id='.$id;
-        if ($search_country_id > 0) $param.= '&search_country_id='.$search_country_id;
-        if ($search_code != '')     $param.= '&search_code='.urlencode($search_country_id);
-        $paramwithsearch = $param;
-        if ($sortorder) $paramwithsearch.= '&sortorder='.$sortorder;
-        if ($sortfield) $paramwithsearch.= '&sortfield='.$sortfield;
-        if (GETPOST('from')) $paramwithsearch.= '&from='.GETPOST('from','alpha');
-        
         // There is several pages
         if ($num > $listlimit)
         {
@@ -1302,10 +1307,10 @@ if ($id)
                     if (empty($reshook)) fieldList($fieldlist,$obj,$tabname[$id],'edit');
 
                     print '<td colspan="3" align="center">';
+                    print '<div name="'.(! empty($obj->rowid)?$obj->rowid:$obj->code).'"></div>';
                     print '<input type="hidden" name="page" value="'.$page.'">';
                     print '<input type="hidden" name="rowid" value="'.$rowid.'">';
                     print '<input type="submit" class="button" name="actionmodify" value="'.$langs->trans("Modify").'">';
-                    print '<div name="'.(! empty($obj->rowid)?$obj->rowid:$obj->code).'"></div>';
                     print '<input type="submit" class="button" name="actioncancel" value="'.$langs->trans("Cancel").'">';
                     print '</td>';
                 }
@@ -1810,13 +1815,14 @@ function fieldList($fieldlist, $obj='', $tabname='', $context='')
 			if ($fieldlist[$field]=='affect') $class='maxwidth50';
 			if ($fieldlist[$field]=='delay') $class='maxwidth50';
 			if ($fieldlist[$field]=='position') $class='maxwidth50';
-			if ($fieldlist[$field]=='libelle') $class='quatrevingtpercent';
+			if ($fieldlist[$field]=='libelle' || $fieldlist[$field]=='label') $class='quatrevingtpercent';
 			if ($fieldlist[$field]=='tracking') $class='quatrevingtpercent';
 			if ($fieldlist[$field]=='sortorder' || $fieldlist[$field]=='sens' || $fieldlist[$field]=='category_type') $class='maxwidth50';
 			print '<td class="'.$classtd.'">';
 			$transfound=0;
 			if (in_array($fieldlist[$field], array('label','libelle')))
 			{
+			    $transkey='';
 			    // Special case for labels
 			    if ($tabname == MAIN_DB_PREFIX.'c_civility') {
 			        $transkey="Civility".strtoupper($obj->code);
@@ -1825,7 +1831,7 @@ function fieldList($fieldlist, $obj='', $tabname='', $context='')
 			        $langs->load("bills");
 			        $transkey="PaymentCondition".strtoupper($obj->code);
 			    }
-			    if ($langs->trans($transkey) != $transkey)
+			    if ($transkey && $langs->trans($transkey) != $transkey)
 			    {
 			        $transfound=1;
 			        print $form->textwithpicto($langs->trans($transkey), $langs->trans("GoIntoTranslationMenuToChangeThis"));

+ 8 - 2
htdocs/admin/expedition.php

@@ -502,8 +502,14 @@ print "<tr class=\"liste_titre\">";
 print "<td>".$langs->trans("Parameter")."</td>\n";
 print "</tr>";
 
+$substitutionarray=pdf_getSubstitutionArray($langs);
+$substitutionarray['__(AnyTranslationKey)__']=$langs->trans("Translation");
+$htmltext = '<i>'.$langs->trans("AvailableVariables").':<br>';
+foreach($substitutionarray as $key => $val)	$htmltext.=$key.'<br>';
+$htmltext.='</i>';
+
 print '<tr><td>';
-print $langs->trans("FreeLegalTextOnShippings").' ('.$langs->trans("AddCRIfTooLong").')<br>';
+print $form->textwithpicto($langs->trans("FreeLegalTextOnShippings"), $langs->trans("AddCRIfTooLong").'<br><br>'.$htmltext).'<br>';
 $variablename='SHIPPING_FREE_TEXT';
 if (empty($conf->global->PDF_ALLOW_HTML_FOR_FREE_TEXT))
 {
@@ -518,7 +524,7 @@ else
 print "</td></tr>\n";
 
 print '<tr><td>';
-print $langs->trans("WatermarkOnDraft").'<br>';
+print $form->textwithpicto($langs->trans("WatermarkOnDraftContractCards"), $htmltext).'<br>';
 print '<input size="50" class="flat" type="text" name="SHIPPING_DRAFT_WATERMARK" value="'.$conf->global->SHIPPING_DRAFT_WATERMARK.'">';
 print "</td></tr>\n";
 

+ 9 - 3
htdocs/admin/expensereport.php

@@ -230,7 +230,7 @@ print load_fiche_titre($langs->trans("ExpenseReportsSetup"),$linkback,'title_set
 
 $head=expensereport_admin_prepare_head();
 
-dol_fiche_head($head, 'expensereport', $langs->trans("ExpenseReports"), 0, 'trip');
+dol_fiche_head($head, 'expensereport', $langs->trans("ExpenseReports"), -1, 'trip');
 
 // Interventions numbering model
 /*
@@ -505,9 +505,15 @@ print '<td align="center" width="60"></td>';
 print "</tr>\n";
 $var=true;
 
+$substitutionarray=pdf_getSubstitutionArray($langs);
+$substitutionarray['__(AnyTranslationKey)__']=$langs->trans("Translation");
+$htmltext = '<i>'.$langs->trans("AvailableVariables").':<br>';
+foreach($substitutionarray as $key => $val)	$htmltext.=$key.'<br>';
+$htmltext.='</i>';
+
 $var=! $var;
 print '<tr class="oddeven"><td colspan="2">';
-print $langs->trans("FreeLegalTextOnExpenseReports").' ('.$langs->trans("AddCRIfTooLong").')<br>';
+print $form->textwithpicto($langs->trans("FreeLegalTextOnExpenseReports"), $langs->trans("AddCRIfTooLong").'<br><br>'.$htmltext).'<br>';
 $variablename='EXPENSEREPORT_FREE_TEXT';
 if (empty($conf->global->PDF_ALLOW_HTML_FOR_FREE_TEXT))
 {
@@ -524,7 +530,7 @@ print '</td></tr>'."\n";
 //Use draft Watermark
 
 print '<tr class="oddeven"><td colspan="2">';
-print $langs->trans("WatermarkOnDraftOrders").'<br>';
+print $form->textwithpicto($langs->trans("WatermarkOnDraftExpenseReports"), $htmltext).'<br>';
 print '<input size="50" class="flat" type="text" name="EXPENSEREPORT_DRAFT_WATERMARK" value="'.$conf->global->EXPENSEREPORT_DRAFT_WATERMARK.'">';
 print '</td></tr>'."\n";
 

+ 1 - 1
htdocs/admin/expensereport_extrafields.php

@@ -74,7 +74,7 @@ print load_fiche_titre($langs->trans("ExpenseReportsSetup"),$linkback,'title_set
 
 $head = expensereport_admin_prepare_head();
 
-dol_fiche_head($head, 'attributes', $langs->trans("ExpenseReports"), 0, 'trip');
+dol_fiche_head($head, 'attributes', $langs->trans("ExpenseReports"), -1, 'trip');
 
 require DOL_DOCUMENT_ROOT.'/core/tpl/admin_extrafields_view.tpl.php';
 

+ 10 - 6
htdocs/admin/external_rss.php

@@ -206,13 +206,13 @@ print '<td>'.$langs->trans("Example").'</td>';
 print '</tr>';
 print '<tr class="impair">';
 print '<td width="100">'.$langs->trans("Title").'</td>';
-print '<td><input type="text" name="external_rss_title_'.($lastexternalrss+1).'" value="'.@constant("EXTERNAL_RSS_TITLE_" . ($lastexternalrss+1)).'" size="64"></td>';
+print '<td><input type="text" class="flat minwidth300" name="external_rss_title_'.($lastexternalrss+1).'" value=""></td>';
 print '<td>'.$langs->trans('RSSUrlExample').'</td>';
 print '</tr>';
 
 print '<tr class="pair">';
 print '<td>'.$langs->trans('RSSUrl').'</td>';
-print '<td><input type="text" name="external_rss_urlrss_'.($lastexternalrss+1).'" value="'.@constant("EXTERNAL_RSS_URLRSS_" . ($lastexternalrss+1)).'" size="64"></td>';
+print '<td><input type="text" class="flat minwidth300" name="external_rss_urlrss_'.($lastexternalrss+1).'" value=""></td>';
 print '<td>http://news.google.com/news?ned=us&topic=h&output=rss<br>http://www.dolibarr.org/rss</td>';
 print '</tr>';
 print '</table>';
@@ -243,10 +243,12 @@ if ($resql)
 
 	    preg_match('/^([0-9]+)/i',$obj->note,$reg);
 		$idrss = $reg[1];
-		//print "x".$idrss;
+        $keyrssurl="EXTERNAL_RSS_URLRSS_".$idrss;
+        $keyrsstitle="EXTERNAL_RSS_URLRSS_".$idrss;
+        //print "x".$idrss;
 
         $rssparser=new RssParser($db);
-		$result = $rssparser->parser(@constant("EXTERNAL_RSS_URLRSS_".$idrss), 5, 300, $conf->externalrss->dir_temp);
+		$result = $rssparser->parser($conf->global->$keyrssurl, 5, 300, $conf->externalrss->dir_temp);
 
 		$var=true;
 
@@ -269,13 +271,13 @@ if ($resql)
 		
 		print '<tr class="oddeven">';
 		print "<td width=\"100px\">".$langs->trans("Title")."</td>";
-		print "<td><input type=\"text\" class=\"flat\" name=\"external_rss_title_" . $idrss . "\" value=\"" . @constant("EXTERNAL_RSS_TITLE_" . $idrss) . "\" size=\"64\"></td>";
+		print "<td><input type=\"text\" class=\"flat minwidth300\" name=\"external_rss_title_" . $idrss . "\" value=\"" . $conf->global->$keyrsstitle . "\"></td>";
 		print "</tr>";
 
 		
 		print '<tr class="oddeven">';
 		print "<td>".$langs->trans("URL")."</td>";
-		print "<td><input type=\"text\" class=\"flat\" name=\"external_rss_urlrss_" . $idrss . "\" value=\"" . @constant("EXTERNAL_RSS_URLRSS_" . $idrss) . "\" size=\"64\"></td>";
+		print "<td><input type=\"text\" class=\"flat minwidth300\" name=\"external_rss_urlrss_" . $idrss . "\" value=\"" . $conf->global->$keyrssurl . "\"></td>";
 		print "</tr>";
 
 		
@@ -304,6 +306,8 @@ if ($resql)
 			print "<td>".$langs->trans("Logo")."</td>";
 			print '<td>';
 			$imageurl=$rssparser->getImageUrl();
+			$linkrss=$rssparser->getLink();
+			if (! preg_match('/^http/', $imageurl)) $imageurl=$linkrss.$imageurl;
 			if ($imageurl) print '<img height="32" src="'.$imageurl.'">';
 			else print $langs->trans("None");
 			print '</td>';

+ 7 - 6
htdocs/admin/facture.php

@@ -733,12 +733,18 @@ print '<input type="submit" class="button" value="'.$langs->trans("Modify").'" /
 print "</td></tr>\n";
 print '</form>';
 
+$substitutionarray=pdf_getSubstitutionArray($langs);
+$substitutionarray['__(AnyTranslationKey)__']=$langs->trans("Translation");
+$htmltext = '<i>'.$langs->trans("AvailableVariables").':<br>';
+foreach($substitutionarray as $key => $val)	$htmltext.=$key.'<br>';
+$htmltext.='</i>';
+
 $var=! $var;
 print '<form action="'.$_SERVER["PHP_SELF"].'" method="POST">';
 print '<input type="hidden" name="token" value="'.$_SESSION['newtoken'].'" />';
 print '<input type="hidden" name="action" value="set_INVOICE_FREE_TEXT" />';
 print '<tr class="oddeven"><td colspan="2">';
-print $langs->trans("FreeLegalTextOnInvoices").' '.img_info($langs->trans("AddCRIfTooLong")).'<br>';
+print $form->textwithpicto($langs->trans("FreeLegalTextOnInvoices"), $langs->trans("AddCRIfTooLong").'<br><br>'.$htmltext).'<br>';
 $variablename='INVOICE_FREE_TEXT';
 if (empty($conf->global->PDF_ALLOW_HTML_FOR_FREE_TEXT))
 {
@@ -760,11 +766,6 @@ print '<form action="'.$_SERVER["PHP_SELF"].'" method="POST">';
 print '<input type="hidden" name="token" value="'.$_SESSION['newtoken'].'" />';
 print '<input type="hidden" name="action" value="set_FACTURE_DRAFT_WATERMARK" />';
 print '<tr class="oddeven"><td>';
-$substitutionarray=pdf_getSubstitutionArray($langs);
-$substitutionarray['__(AnyTranslationKey)__']=$langs->trans("Translation");
-$htmltext = '<i>'.$langs->trans("AvailableVariables").':<br>';
-foreach($substitutionarray as $key => $val)	$htmltext.=$key.'<br>';
-$htmltext.='</i>';
 print $form->textwithpicto($langs->trans("WatermarkOnDraftBill"), $htmltext);
 print '</td>';
 print '<td><input size="50" class="flat" type="text" name="FACTURE_DRAFT_WATERMARK" value="'.$conf->global->FACTURE_DRAFT_WATERMARK.'" />';

+ 10 - 3
htdocs/admin/fichinter.php

@@ -265,7 +265,7 @@ print load_fiche_titre($langs->trans("InterventionsSetup"),$linkback,'title_setu
 
 $head=fichinter_admin_prepare_head();
 
-dol_fiche_head($head, 'ficheinter', $langs->trans("Interventions"), 0, 'intervention');
+dol_fiche_head($head, 'ficheinter', $langs->trans("Interventions"), -1, 'intervention');
 
 // Interventions numbering model
 
@@ -531,11 +531,17 @@ print '<td align="center" width="60">'.$langs->trans("Value").'</td>';
 print "<td>&nbsp;</td>\n";
 print "</tr>\n";
 
+$substitutionarray=pdf_getSubstitutionArray($langs);
+$substitutionarray['__(AnyTranslationKey)__']=$langs->trans("Translation");
+$htmltext = '<i>'.$langs->trans("AvailableVariables").':<br>';
+foreach($substitutionarray as $key => $val)	$htmltext.=$key.'<br>';
+$htmltext.='</i>';
+
 print '<form action="'.$_SERVER["PHP_SELF"].'" method="post">';
 print '<input type="hidden" name="token" value="'.$_SESSION['newtoken'].'">';
 print '<input type="hidden" name="action" value="set_FICHINTER_FREE_TEXT">';
 print '<tr class="oddeven"><td colspan="2">';
-print $langs->trans("FreeLegalTextOnInterventions").' '.img_info($langs->trans("AddCRIfTooLong")).'<br>';
+print $form->textwithpicto($langs->trans("FreeLegalTextOnInterventions"), $langs->trans("AddCRIfTooLong").'<br><br>'.$htmltext).'<br>';
 $variablename='FICHINTER_FREE_TEXT';
 if (empty($conf->global->PDF_ALLOW_HTML_FOR_FREE_TEXT))
 {
@@ -558,7 +564,8 @@ print "<form method=\"post\" action=\"".$_SERVER["PHP_SELF"]."\">";
 print '<input type="hidden" name="token" value="'.$_SESSION['newtoken'].'">';
 print "<input type=\"hidden\" name=\"action\" value=\"set_FICHINTER_DRAFT_WATERMARK\">";
 print '<tr class="oddeven"><td>';
-print $langs->trans("WatermarkOnDraftInterventionCards").'</td><td>';
+print $form->textwithpicto($langs->trans("WatermarkOnDraftInterventionCards"), $htmltext).'<br>';
+print '</td><td>';
 print '<input size="50" class="flat" type="text" name="FICHINTER_DRAFT_WATERMARK" value="'.$conf->global->FICHINTER_DRAFT_WATERMARK.'">';
 print '</td><td align="right">';
 print '<input type="submit" class="button" value="'.$langs->trans("Modify").'">';

+ 7 - 1
htdocs/admin/livraison.php

@@ -479,12 +479,18 @@ print '<td width="80">&nbsp;</td>';
 print "</tr>\n";
 $var=true;
 
+$substitutionarray=pdf_getSubstitutionArray($langs);
+$substitutionarray['__(AnyTranslationKey)__']=$langs->trans("Translation");
+$htmltext = '<i>'.$langs->trans("AvailableVariables").':<br>';
+foreach($substitutionarray as $key => $val)	$htmltext.=$key.'<br>';
+$htmltext.='</i>';
+
 $var=! $var;
 print '<form action="'.$_SERVER["PHP_SELF"].'" method="post">';
 print '<input type="hidden" name="token" value="'.$_SESSION['newtoken'].'">';
 print '<input type="hidden" name="action" value="set_DELIVERY_FREE_TEXT">';
 print '<tr class="oddeven"><td colspan="2">';
-print $langs->trans("FreeLegalTextOnDeliveryReceipts").' ('.$langs->trans("AddCRIfTooLong").')<br>';
+print $form->textwithpicto($langs->trans("FreeLegalTextOnDeliveryReceipts"), $langs->trans("AddCRIfTooLong").'<br><br>'.$htmltext).'<br>';
 $variablename='DELIVERY_FREE_TEXT';
 if (empty($conf->global->PDF_ALLOW_HTML_FOR_FREE_TEXT))
 {

+ 1 - 3
htdocs/admin/loan.php

@@ -96,9 +96,7 @@ print "</tr>\n";
 
 foreach ($list as $key)
 {
-	
-
-	print '<tr '.$bc[$var].' class="value">';
+	print '<tr class="oddeven value">';
 
 	// Param
 	$label = $langs->trans($key); 

+ 19 - 12
htdocs/admin/mails_templates.php

@@ -4,7 +4,7 @@
  * Copyright (C) 2004       Benoit Mortier          <benoit.mortier@opensides.be>
  * Copyright (C) 2005-2012  Regis Houssin           <regis.houssin@capnetworks.com>
  * Copyright (C) 2010-2016  Juanjo Menent           <jmenent@2byte.es>
- * Copyright (C) 2011-2015  Philippe Grand          <philippe.grand@atoo-net.com>
+ * Copyright (C) 2011-2017  Philippe Grand          <philippe.grand@atoo-net.com>
  * Copyright (C) 2011       Remy Younes             <ryounes@gmail.com>
  * Copyright (C) 2012-2015  Marcos García           <marcosgdf@gmail.com>
  * Copyright (C) 2012       Christophe Battarel     <christophe.battarel@ltairis.fr>
@@ -118,17 +118,17 @@ require_once DOL_DOCUMENT_ROOT.'/core/class/html.formmail.class.php';
 $formmail=new FormMail($db);
 if (empty($conf->global->MAIN_EMAIL_TEMPLATES_FOR_OBJECT_LINES))
 {
-    $tmp=$formmail->getAvailableSubstitKey('form');
+    $tmp=FormMail::getAvailableSubstitKey('formemail');
     $tmp['__(AnyTransKey)__']='__(AnyTransKey)__';
     $helpsubstit = $langs->trans("AvailableVariables").':<br>'.implode('<br>', $tmp);
     $helpsubstitforlines = $langs->trans("AvailableVariables").':<br>'.implode('<br>', $tmp);
 }
 else
 {
-    $tmp=$formmail->getAvailableSubstitKey('formwithlines');
+    $tmp=FormMail::getAvailableSubstitKey('formemailwithlines');
     $tmp['__(AnyTransKey)__']='__(AnyTransKey)__';
     $helpsubstit = $langs->trans("AvailableVariables").':<br>'.implode('<br>', $tmp);
-    $tmp=$formmail->getAvailableSubstitKey('formforlines');
+    $tmp=FormMail::getAvailableSubstitKey('formemailforlines');
     $helpsubstitforlines = $langs->trans("AvailableVariables").':<br>'.implode('<br>', $tmp);
 }
 
@@ -484,7 +484,11 @@ if ($action != 'edit')
         {
             print '<td align="'.$align.'">';
         	if (! empty($tabhelp[$id][$value]) && preg_match('/^http(s*):/i',$tabhelp[$id][$value])) print '<a href="'.$tabhelp[$id][$value].'" target="_blank">'.$valuetoshow.' '.img_help(1,$valuetoshow).'</a>';
-        	else if (! empty($tabhelp[$id][$value])) print $form->textwithpicto($valuetoshow,$tabhelp[$id][$value]);
+        	else if (! empty($tabhelp[$id][$value])) 
+        	{
+        	    if (in_array($value, array('topic'))) print $form->textwithpicto($valuetoshow, $tabhelp[$id][$value], 1, 'help', '', 0, 2, $value);   // Tooltip on click
+        	    else print $form->textwithpicto($valuetoshow, $tabhelp[$id][$value], 1, 'help', '', 0, 2);                             // Tooltip on hover
+        	}
         	else print $valuetoshow;
             print '</td>';
          }
@@ -539,8 +543,8 @@ if ($action != 'edit')
     foreach ($fieldsforcontent as $tmpfieldlist)
     {
         print '<tr class="impair nodrag nodrop nohover"><td colspan="5">';
-        if ($tmpfieldlist == 'content') print '<strong>'.$form->textwithpicto($langs->trans("Content"),$tabhelp[$id][$tmpfieldlist]).'</strong><br>';
-        if ($tmpfieldlist == 'content_lines') print '<strong>'.$form->textwithpicto($langs->trans("ContentForLines"),$tabhelp[$id][$tmpfieldlist]).'</strong><br>';
+        if ($tmpfieldlist == 'content') print '<strong>'.$form->textwithpicto($langs->trans("Content"), $tabhelp[$id][$tmpfieldlist], 1, 'help', '', 0, 2, $tmpfieldlist).'</strong><br>';
+        if ($tmpfieldlist == 'content_lines') print '<strong>'.$form->textwithpicto($langs->trans("ContentForLines"), $tabhelp[$id][$tmpfieldlist], 1, 'help', '', 0, 2, $tmpfieldlist).'</strong><br>';
     
         if ($context != 'hide')
         {
@@ -624,7 +628,11 @@ if ($resql)
         // Affiche nom du champ
         if ($showfield)
         {
-            if (! empty($tabhelp[$id][$value])) $valuetoshow = $form->textwithpicto($valuetoshow,$tabhelp[$id][$value]);
+            if (! empty($tabhelp[$id][$value])) 
+            {
+                if (in_array($value, array('topic'))) $valuetoshow = $form->textwithpicto($valuetoshow, $tabhelp[$id][$value], 1, 'help', '', 0, 2);   // Tooltip on hover
+                else $valuetoshow = $form->textwithpicto($valuetoshow, $tabhelp[$id][$value], 1, 'help', '', 0, 2);                             // Tooltip on hover
+            }
             print getTitleFieldOfList($valuetoshow, 0, $_SERVER["PHP_SELF"], ($sortable?$fieldlist[$field]:''), ($page?'page='.$page.'&':''), $param, "align=".$align, $sortfield, $sortorder);
         }
     }
@@ -651,11 +659,10 @@ if ($resql)
         // Lines with values
         while ($i < $num)
         {
-            $var = ! $var;
 
             $obj = $db->fetch_object($resql);
             //print_r($obj);
-            print '<tr '.$bc[$var].' id="rowid-'.$obj->rowid.'">';
+            print '<tr class="oddeven" id="rowid-'.$obj->rowid.'">';
             if ($action == 'edit' && ($rowid == (! empty($obj->rowid)?$obj->rowid:$obj->code)))
             {
                 $tmpaction='edit';
@@ -689,7 +696,7 @@ if ($resql)
                     // Show value for field
                     if ($showfield) {
                 
-                        print '</tr><tr '.$bc[$var].' nohover tr-'.$tmpfieldlist.'-'.$rowid.' "><td colspan="5">'; // To create an artificial CR for the current tr we are on
+                        print '</tr><tr class="oddeven" nohover tr-'.$tmpfieldlist.'-'.$rowid.' "><td colspan="5">'; // To create an artificial CR for the current tr we are on
                         $okforextended = true;
                         if (empty($conf->global->FCKEDITOR_ENABLE_MAIL))
                             $okforextended = false;
@@ -781,7 +788,7 @@ if ($resql)
                     // Show value for field
                     if ($showfield) {
                         
-                        print '</tr><tr '.$bc[$var].' nohover tr-'.$tmpfieldlist.'-'.$i.' "><td colspan="5">'; // To create an artificial CR for the current tr we are on
+                        print '</tr><tr class="oddeven" nohover tr-'.$tmpfieldlist.'-'.$i.' "><td colspan="5">'; // To create an artificial CR for the current tr we are on
                         $okforextended = true;
                         if (empty($conf->global->FCKEDITOR_ENABLE_MAIL))
                             $okforextended = false;

+ 2 - 4
htdocs/admin/modules.php

@@ -428,8 +428,6 @@ $h++;
 print "<br>\n";
 
 
-$var=true;
-
 if ($mode == 'common')
 {
     
@@ -782,7 +780,7 @@ if ($mode == 'marketplace')
     print '</tr>';
 
     
-    print "<tr ".$bc[$var].">\n";
+    print "<tr class=\"oddeven\">\n";
     $url='https://www.dolistore.com';
     print '<td align="left"><a href="'.$url.'" target="_blank" rel="external"><img border="0" class="imgautosize imgmaxwidth180" src="'.DOL_URL_ROOT.'/theme/dolistore_logo.png"></a></td>';
     print '<td>'.$langs->trans("DoliStoreDesc").'</td>';
@@ -790,7 +788,7 @@ if ($mode == 'marketplace')
     print '</tr>';
 
     
-    print "<tr ".$bc[$var].">\n";
+    print "<tr class=\"oddeven\">\n";
     $url='https://partners.dolibarr.org';
     print '<td align="left"><a href="'.$url.'" target="_blank" rel="external"><img border="0" class="imgautosize imgmaxwidth180" src="'.DOL_URL_ROOT.'/theme/dolibarr_preferred_partner_int.png"></a></td>';
     print '<td>'.$langs->trans("DoliPartnersDesc").'</td>';

+ 4 - 9
htdocs/admin/oauth.php

@@ -93,7 +93,6 @@ print $langs->trans("ListOfSupportedOauthProviders").'<br><br>';
 
 print '<table class="noborder" width="100%">';
 
-$var = true;
 $i=0;
 
 foreach ($list as $key)
@@ -116,31 +115,27 @@ foreach ($list as $key)
     if ($supported)
     {
         $redirect_uri=$urlwithroot.'/core/modules/oauth/'.$supportedoauth2array[$key[0]].'_oauthcallback.php';
-        $var = !$var;
-        print '<tr '.$bc[$var].' class="value">';
+        print '<tr class="oddeven value">';
         print '<td>'.$langs->trans("UseTheFollowingUrlAsRedirectURI").'</td>';
         print '<td><input style="width: 80%" type"text" name="uri'.$key[0].'" value="'.$redirect_uri.'">';
         print '</td></tr>';
     }
     else
     {
-        $var = !$var;
-        print '<tr '.$bc[$var].' class="value">';
+        print '<tr class="oddeven value">';
         print '<td>'.$langs->trans("UseTheFollowingUrlAsRedirectURI").'</td>';
         print '<td>'.$langs->trans("FeatureNotYetSupported").'</td>';
         print '</td></tr>';
     }
         
     // Api Id
-    $var = !$var;
-    print '<tr '.$bc[$var].' class="value">';
+    print '<tr class="oddeven value">';
     print '<td><label for="'.$key[1].'">'.$langs->trans($key[1]).'</label></td>';
     print '<td><input type="text" size="100" id="'.$key[1].'" name="'.$key[1].'" value="'.$conf->global->{$key[1]}.'">';
     print '</td></tr>';
 
     // Api Secret
-    $var = !$var;
-    print '<tr '.$bc[$var].' class="value">';
+    print '<tr class="oddeven value">';
     print '<td><label for="'.$key[2].'">'.$langs->trans($key[2]).'</label></td>';
     print '<td><input type="password" size="100" id="'.$key[2].'" name="'.$key[2].'" value="'.$conf->global->{$key[2]}.'">';
     print '</td></tr>';

+ 1 - 1
htdocs/admin/payment.php

@@ -117,7 +117,7 @@ $linkback='<a href="'.DOL_URL_ROOT.'/admin/modules.php">'.$langs->trans("BackToM
 print load_fiche_titre($langs->trans("BillsSetup"),$linkback,'title_setup');
 
 $head = invoice_admin_prepare_head();
-dol_fiche_head($head, 'payment', $langs->trans("Invoices"), 0, 'invoice');
+dol_fiche_head($head, 'payment', $langs->trans("Invoices"), -1, 'invoice');
 
 /*
  *  Numbering module

+ 42 - 3
htdocs/admin/pdf.php

@@ -42,15 +42,25 @@ $langs->load("members");
 if (! $user->admin) accessforbidden();
 
 $action = GETPOST('action','alpha');
+$cancel = GETPOST('cancel','alpha');
+
 
 /*
  * Actions
  */
 
+if ($cancel) {
+    $action='';
+}
+
 if ($action == 'update')
 {
 	dolibarr_set_const($db, "MAIN_PDF_FORMAT",    $_POST["MAIN_PDF_FORMAT"],'chaine',0,'',$conf->entity);
-
+	
+	dolibarr_set_const($db, "MAIN_PDF_MARGIN_LEFT",    $_POST["MAIN_PDF_MARGIN_LEFT"],'chaine',0,'',$conf->entity);
+	dolibarr_set_const($db, "MAIN_PDF_MARGIN_RIGHT",   $_POST["MAIN_PDF_MARGIN_RIGHT"],'chaine',0,'',$conf->entity);
+	dolibarr_set_const($db, "MAIN_PDF_MARGIN_TOP",     $_POST["MAIN_PDF_MARGIN_TOP"],'chaine',0,'',$conf->entity);
+	dolibarr_set_const($db, "MAIN_PDF_MARGIN_BOTTOM",  $_POST["MAIN_PDF_MARGIN_BOTTOM"],'chaine',0,'',$conf->entity);
 
     dolibarr_set_const($db, "MAIN_PROFID1_IN_ADDRESS",    $_POST["MAIN_PROFID1_IN_ADDRESS"],'chaine',0,'',$conf->entity);
 	dolibarr_set_const($db, "MAIN_PROFID2_IN_ADDRESS",    $_POST["MAIN_PROFID2_IN_ADDRESS"],'chaine',0,'',$conf->entity);
@@ -133,7 +143,20 @@ if ($action == 'edit')	// Edit
     print $formadmin->select_paper_format($selected,'MAIN_PDF_FORMAT');
     print '</td></tr>';
 
-	print '</table>';
+    print '<tr class="oddeven"><td>'.$langs->trans("MAIN_PDF_MARGIN_LEFT").'</td><td>';
+    print '<input type="text" class="maxwidth50" name="MAIN_PDF_MARGIN_LEFT" value="'.(empty($conf->global->MAIN_PDF_MARGIN_LEFT)?10:$conf->global->MAIN_PDF_MARGIN_LEFT).'">';
+    print '</td></tr>';
+    print '<tr class="oddeven"><td>'.$langs->trans("MAIN_PDF_MARGIN_RIGHT").'</td><td>';
+    print '<input type="text" class="maxwidth50" name="MAIN_PDF_MARGIN_RIGHT" value="'.(empty($conf->global->MAIN_PDF_MARGIN_RIGHT)?10:$conf->global->MAIN_PDF_MARGIN_RIGHT).'">';
+    print '</td></tr>';
+    print '<tr class="oddeven"><td>'.$langs->trans("MAIN_PDF_MARGIN_TOP").'</td><td>';
+    print '<input type="text" class="maxwidth50" name="MAIN_PDF_MARGIN_TOP" value="'.(empty($conf->global->MAIN_PDF_MARGIN_TOP)?10:$conf->global->MAIN_PDF_MARGIN_TOP).'">';
+    print '</td></tr>';
+    print '<tr class="oddeven"><td>'.$langs->trans("MAIN_PDF_MARGIN_BOTTOM").'</td><td>';
+    print '<input type="text" class="maxwidth50" name="MAIN_PDF_MARGIN_BOTTOM" value="'.(empty($conf->global->MAIN_PDF_MARGIN_BOTTOM)?10:$conf->global->MAIN_PDF_MARGIN_BOTTOM).'">';
+    print '</td></tr>';
+    
+    print '</table>';
 
 	print '<br>';
 
@@ -270,7 +293,9 @@ if ($action == 'edit')	// Edit
 	print '</table>';
 
     print '<br><div class="center">';
-    print '<input class="button" type="submit" value="'.$langs->trans("Save").'">';
+    print '<input class="button" type="submit" name="save" value="'.$langs->trans("Save").'">';
+    print ' &nbsp; ';
+    print '<input class="button" type="submit" name="cancel" value="'.$langs->trans("Cancel").'">';
     print '</div>';
 
     print '</form>';
@@ -314,6 +339,20 @@ else	// Show
     print $pdfformatlabel;
     print '</td></tr>';
 
+    print '<tr class="oddeven"><td>'.$langs->trans("MAIN_PDF_MARGIN_LEFT").'</td><td>';
+    print empty($conf->global->MAIN_PDF_MARGIN_LEFT)?10:$conf->global->MAIN_PDF_MARGIN_LEFT;
+    print '</td></tr>';
+    print '<tr class="oddeven"><td>'.$langs->trans("MAIN_PDF_MARGIN_RIGHT").'</td><td>';
+    print empty($conf->global->MAIN_PDF_MARGIN_RIGHT)?10:$conf->global->MAIN_PDF_MARGIN_RIGHT;
+    print '</td></tr>';
+    print '<tr class="oddeven"><td>'.$langs->trans("MAIN_PDF_MARGIN_TOP").'</td><td>';
+    print empty($conf->global->MAIN_PDF_MARGIN_TOP)?10:$conf->global->MAIN_PDF_MARGIN_TOP;
+    print '</td></tr>';
+    print '<tr class="oddeven"><td>'.$langs->trans("MAIN_PDF_MARGIN_BOTTOM").'</td><td>';
+    print empty($conf->global->MAIN_PDF_MARGIN_BOTTOM)?10:$conf->global->MAIN_PDF_MARGIN_BOTTOM;
+    print '</td></tr>';
+    
+    
 	print '</table>';
 
 	print '<br>';

+ 1 - 3
htdocs/admin/perms.php

@@ -140,7 +140,6 @@ if ($result)
 {
     $num	= $db->num_rows($result);
     $i		= 0;
-    $var	= True;
     $oldmod	= "";
 
     while ($i < $num)
@@ -187,8 +186,7 @@ if ($result)
         }
 
         
-        print '<tr '. $bc[$var].'>';
-
+        print '<tr class="oddeven">';
         print '<td>'.img_object('',$picto).' '.$objMod->getName();
         print '<a name="'.$objMod->getName().'">&nbsp;</a>';
 

+ 8 - 7
htdocs/admin/propal.php

@@ -570,11 +570,17 @@ print "</td></tr>\n";
 print '</form>';
 */
 
+$substitutionarray=pdf_getSubstitutionArray($langs);
+$substitutionarray['__(AnyTranslationKey)__']=$langs->trans("Translation");
+$htmltext = '<i>'.$langs->trans("AvailableVariables").':<br>';
+foreach($substitutionarray as $key => $val)	$htmltext.=$key.'<br>';
+$htmltext.='</i>';
+
 print '<form action="'.$_SERVER["PHP_SELF"].'" method="post">';
 print '<input type="hidden" name="token" value="'.$_SESSION['newtoken'].'">';
 print '<input type="hidden" name="action" value="set_PROPOSAL_FREE_TEXT">';
 print '<tr class="oddeven"><td colspan="2">';
-print $langs->trans("FreeLegalTextOnProposal").' '.img_info($langs->trans("AddCRIfTooLong")).'<br>';
+print $form->textwithpicto($langs->trans("FreeLegalTextOnProposal"), $langs->trans("AddCRIfTooLong").'<br><br>'.$htmltext).'<br>';
 $variablename='PROPOSAL_FREE_TEXT';
 if (empty($conf->global->PDF_ALLOW_HTML_FOR_FREE_TEXT))
 {
@@ -596,12 +602,7 @@ print "<form method=\"post\" action=\"".$_SERVER["PHP_SELF"]."\">";
 print '<input type="hidden" name="token" value="'.$_SESSION['newtoken'].'">';
 print "<input type=\"hidden\" name=\"action\" value=\"set_PROPALE_DRAFT_WATERMARK\">";
 print '<tr class="oddeven"><td>';
-$substitutionarray=pdf_getSubstitutionArray($langs);
-$substitutionarray['__(AnyTranslationKey)__']=$langs->trans("Translation");
-$htmltext = '<i>'.$langs->trans("AvailableVariables").':<br>';
-foreach($substitutionarray as $key => $val)	$htmltext.=$key.'<br>';
-$htmltext.='</i>';
-print $form->textwithpicto($langs->trans("WatermarkOnDraftProposal"), $htmltext);
+print $form->textwithpicto($langs->trans("WatermarkOnDraftProposal"), $htmltext).'<br>';
 print '</td><td>';
 print '<input class="flat minwidth200" type="text" name="PROPALE_DRAFT_WATERMARK" value="'.$conf->global->PROPALE_DRAFT_WATERMARK.'">';
 print '</td><td align="right">';

+ 1 - 1
htdocs/admin/salaries.php

@@ -99,7 +99,7 @@ foreach ($list as $key)
 {
 	
 
-	print '<tr '.$bc[$var].' class="value">';
+	print '<tr class="oddeven value">';
 
 	// Param
 	$label = $langs->trans($key);

+ 56 - 0
htdocs/admin/stock.php

@@ -432,6 +432,61 @@ if ($virtualdiffersfromphysical)
 }
 
 
+print '<br />';
+if ($conf->global->MAIN_LEVEL_FEATURES >= 2)
+{
+	$var=false;
+	print '<table class="noborder" width="100%">';
+	print '<tr class="liste_titre">';
+	print '<td>'.$langs->trans("Inventory").'</td>'."\n";
+	print '<td align="center" width="20">&nbsp;</td>';
+	print '<td align="center" width="100">&nbsp;</td>'."\n";
+	
+	// Example with a yes / no select
+	$var=!$var;
+	print '<tr '.$bc[$var].'>';
+	print '<td>'.$langs->trans("INVENTORY_DISABLE_VIRTUAL").'</td>';
+	print '<td align="center" width="20">&nbsp;</td>';
+	print '<td align="right" width="300">';
+	print '<form method="POST" action="'.$_SERVER['PHP_SELF'].'">';
+	print '<input type="hidden" name="token" value="'.$_SESSION['newtoken'].'">';
+	print '<input type="hidden" name="action" value="set_INVENTORY_DISABLE_VIRTUAL">';
+	print $form->selectyesno("INVENTORY_DISABLE_VIRTUAL",$conf->global->INVENTORY_DISABLE_VIRTUAL,1);
+	print '<input type="submit" class="button" value="'.$langs->trans("Modify").'">';
+	print '</form>';
+	print '</td></tr>';
+	
+	// Example with a yes / no select
+	$var=!$var;
+	print '<tr '.$bc[$var].'>';
+	print '<td>'.$langs->trans("INVENTORY_USE_MIN_PA_IF_NO_LAST_PA").'</td>';
+	print '<td align="center" width="20">&nbsp;</td>';
+	print '<td align="right" width="300">';
+	print '<form method="POST" action="'.$_SERVER['PHP_SELF'].'">';
+	print '<input type="hidden" name="token" value="'.$_SESSION['newtoken'].'">';
+	print '<input type="hidden" name="action" value="set_INVENTORY_USE_MIN_PA_IF_NO_LAST_PA">';
+	print $form->selectyesno("INVENTORY_USE_MIN_PA_IF_NO_LAST_PA",$conf->global->INVENTORY_USE_MIN_PA_IF_NO_LAST_PA,1);
+	print '<input type="submit" class="button" value="'.$langs->trans("Modify").'">';
+	print '</form>';
+	print '</td></tr>';
+	
+	// Example with a yes / no select
+	$var=!$var;
+	print '<tr '.$bc[$var].'>';
+	print '<td>'.$langs->trans("INVENTORY_USE_INVENTORY_DATE_FROM_DATEMVT").'</td>';
+	print '<td align="center" width="20">&nbsp;</td>';
+	print '<td align="right" width="300">';
+	print '<form method="POST" action="'.$_SERVER['PHP_SELF'].'">';
+	print '<input type="hidden" name="token" value="'.$_SESSION['newtoken'].'">';
+	print '<input type="hidden" name="action" value="set_INVENTORY_USE_INVENTORY_DATE_FROM_DATEMVT">';
+	print $form->selectyesno("INVENTORY_USE_INVENTORY_DATE_FROM_DATEMVT",$conf->global->INVENTORY_USE_INVENTORY_DATE_FROM_DATEMVT,1);
+	print '<input type="submit" class="button" value="'.$langs->trans("Modify").'">';
+	print '</form>';
+	print '</td></tr>';
+	
+	print '</table>';
+}
+
 $var=true;
 print '<table class="noborder" width="100%">';
 
@@ -508,6 +563,7 @@ if ($conf->global->PRODUIT_SOUSPRODUITS)
 
 print '</table>';
 
+
 llxFooter();
 
 $db->close();

+ 9 - 4
htdocs/admin/supplier_invoice.php

@@ -5,7 +5,7 @@
  * Copyright (C) 2004      Sebastien Di Cintio     <sdicintio@ressource-toi.org>
  * Copyright (C) 2004      Benoit Mortier          <benoit.mortier@opensides.be>
  * Copyright (C) 2010-2013 Juanjo Menent           <jmenent@2byte.es>
- * Copyright (C) 2011-2015 Philippe Grand          <philippe.grand@atoo-net.com>
+ * Copyright (C) 2011-2017 Philippe Grand          <philippe.grand@atoo-net.com>
  *
  * 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
@@ -361,7 +361,6 @@ foreach ($dirmodels as $reldir)
 
     if (is_dir($dir))
     {
-        $var=true;
 
         $handle=opendir($dir);
 
@@ -379,7 +378,7 @@ foreach ($dirmodels as $reldir)
 	                $module = new $classname($db, new FactureFournisseur($db));
 
                     
-                    print "<tr ".$bc[$var].">\n";
+                    print "<tr class=\"oddeven\">\n";
                     print "<td>";
 	                print (empty($module->name)?$name:$module->name);
 	                print "</td>\n";
@@ -471,8 +470,14 @@ print '<td align="center" width="60">'.$langs->trans("Value").'</td>';
 print '<td width="80">&nbsp;</td>';
 print "</tr>\n";
 
+$substitutionarray=pdf_getSubstitutionArray($langs);
+$substitutionarray['__(AnyTranslationKey)__']=$langs->trans("Translation");
+$htmltext = '<i>'.$langs->trans("AvailableVariables").':<br>';
+foreach($substitutionarray as $key => $val)	$htmltext.=$key.'<br>';
+$htmltext.='</i>';
+
 print '<tr class="oddeven"><td colspan="2">';
-print $langs->trans("FreeLegalTextOnInvoices").' '.img_info($langs->trans("AddCRIfTooLong")).'</br>';
+print $form->textwithpicto($langs->trans("FreeLegalTextOnInvoices"), $langs->trans("AddCRIfTooLong").'<br><br>'.$htmltext).'<br>';
 $variablename='SUPPLIER_INVOICE_FREE_TEXT';
 if (empty($conf->global->PDF_ALLOW_HTML_FOR_FREE_TEXT))
 {

+ 10 - 4
htdocs/admin/supplier_order.php

@@ -5,7 +5,7 @@
  * Copyright (C) 2004      Sebastien Di Cintio     <sdicintio@ressource-toi.org>
  * Copyright (C) 2004      Benoit Mortier          <benoit.mortier@opensides.be>
  * Copyright (C) 2010-2013 Juanjo Menent           <jmenent@2byte.es>
- * Copyright (C) 2011-2015 Philippe Grand          <philippe.grand@atoo-net.com>
+ * Copyright (C) 2011-2017 Philippe Grand          <philippe.grand@atoo-net.com>
  *
  * 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
@@ -382,7 +382,6 @@ print '</tr>'."\n";
 
 clearstatcache();
 
-$var=true;
 foreach ($dirmodels as $reldir)
 {
 	$dir = dol_buildpath($reldir."core/modules/supplier_order/pdf/");
@@ -403,7 +402,7 @@ foreach ($dirmodels as $reldir)
 	                $module = new $classname($db, new CommandeFournisseur($db));
 
                     
-                    print "<tr ".$bc[$var].">\n";
+                    print "<tr class=\"oddeven\">\n";
                     print "<td>";
 	                print (empty($module->name)?$name:$module->name);
 	                print "</td>\n";
@@ -491,6 +490,7 @@ 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)
 //{
 	print '<tr class="oddeven"><td>';
@@ -536,8 +536,14 @@ else
 }
 */
 
+$substitutionarray=pdf_getSubstitutionArray($langs);
+$substitutionarray['__(AnyTranslationKey)__']=$langs->trans("Translation");
+$htmltext = '<i>'.$langs->trans("AvailableVariables").':<br>';
+foreach($substitutionarray as $key => $val)	$htmltext.=$key.'<br>';
+$htmltext.='</i>';
+
 print '<tr class="oddeven"><td colspan="2">';
-print $langs->trans("FreeLegalTextOnOrders").' '.img_info($langs->trans("AddCRIfTooLong")).'<br>';
+print $form->textwithpicto($langs->trans("FreeLegalTextOnOrders"), $langs->trans("AddCRIfTooLong").'<br><br>'.$htmltext).'<br>';
 $variablename='SUPPLIER_ORDER_FREE_TEXT';
 if (empty($conf->global->PDF_ALLOW_HTML_FOR_FREE_TEXT))
 {

+ 1 - 2
htdocs/admin/supplier_payment.php

@@ -367,7 +367,6 @@ foreach ($dirmodels as $reldir)
 
     if (is_dir($dir))
     {
-        $var=true;
 
         $handle=opendir($dir);
 
@@ -385,7 +384,7 @@ foreach ($dirmodels as $reldir)
 	                $module = new $classname($db, new PaiementFourn($db));
 
                     
-                    print "<tr ".$bc[$var].">\n";
+                    print "<tr class=\"oddeven\">\n";
                     print "<td>";
 	                print (empty($module->name)?$name:$module->name);
 	                print "</td>\n";

+ 9 - 2
htdocs/admin/supplier_proposal.php

@@ -523,12 +523,18 @@ print '<td width="60" align="center">'.$langs->trans("Value")."</td>\n";
 print "<td>&nbsp;</td>\n";
 print "</tr>";
 
+$substitutionarray=pdf_getSubstitutionArray($langs);
+$substitutionarray['__(AnyTranslationKey)__']=$langs->trans("Translation");
+$htmltext = '<i>'.$langs->trans("AvailableVariables").':<br>';
+foreach($substitutionarray as $key => $val)	$htmltext.=$key.'<br>';
+$htmltext.='</i>';
+
 $var=! $var;
 print '<form action="'.$_SERVER["PHP_SELF"].'" method="post">';
 print '<input type="hidden" name="token" value="'.$_SESSION['newtoken'].'">';
 print '<input type="hidden" name="action" value="set_SUPPLIER_PROPOSAL_FREE_TEXT">';
 print '<tr class="oddeven"><td colspan="2">';
-print $langs->trans("FreeLegalTextOnSupplierProposal").' '.img_info($langs->trans("AddCRIfTooLong")).'</br>';
+print $form->textwithpicto($langs->trans("FreeLegalTextOnSupplierProposal"), $langs->trans("AddCRIfTooLong").'<br><br>'.$htmltext).'<br>';
 $variablename='SUPPLIER_PROPOSAL_FREE_TEXT';
 if (empty($conf->global->PDF_ALLOW_HTML_FOR_FREE_TEXT))
 {
@@ -550,7 +556,8 @@ print "<form method=\"post\" action=\"".$_SERVER["PHP_SELF"]."\">";
 print '<input type="hidden" name="token" value="'.$_SESSION['newtoken'].'">';
 print "<input type=\"hidden\" name=\"action\" value=\"set_SUPPLIER_PROPOSAL_DRAFT_WATERMARK\">";
 print '<tr class="oddeven"><td>';
-print $langs->trans("WatermarkOnDraftSupplierProposal").'</td><td>';
+print $form->textwithpicto($langs->trans("WatermarkOnDraftProposal"), $htmltext).'<br>';
+print '</td><td>';
 print '<input size="50" class="flat" type="text" name="SUPPLIER_PROPOSAL_DRAFT_WATERMARK" value="'.$conf->global->SUPPLIER_PROPOSAL_DRAFT_WATERMARK.'">';
 print '</td><td align="right">';
 print '<input type="submit" class="button" value="'.$langs->trans("Modify").'">';

+ 14 - 8
htdocs/admin/syslog.php

@@ -40,7 +40,7 @@ $action = GETPOST("action");
 $syslogModules = array();
 $activeModules = array();
 
-if (defined('SYSLOG_HANDLERS')) $activeModules = json_decode(constant('SYSLOG_HANDLERS'));
+if (! empty($conf->global->SYSLOG_HANDLERS)) $activeModules = json_decode($conf->global->SYSLOG_HANDLERS);
 
 $dirsyslogs = array_merge(array('/core/modules/syslog/'), $conf->modules_parts['syslog']);
 foreach ($dirsyslogs as $reldir) {
@@ -88,7 +88,8 @@ if ($action == 'set')
 
 	$newActiveModules = array();
 	$selectedModules = (isset($_POST['SYSLOG_HANDLERS']) ? $_POST['SYSLOG_HANDLERS'] : array());
-	//var_dump($selectedModules);
+	
+	// Save options of handler
 	foreach ($syslogModules as $syslogHandler)
 	{
 		if (in_array($syslogHandler, $syslogModules))
@@ -101,7 +102,7 @@ if ($action == 'set')
 				if (isset($_POST[$option['constant']]))
 				{
 					$_POST[$option['constant']] = trim($_POST[$option['constant']]);
-					dolibarr_del_const($db, $option['constant'], 0);
+					dolibarr_del_const($db, $option['constant'], -1);
 					dolibarr_set_const($db, $option['constant'], $_POST[$option['constant']], 'chaine',0, '', 0);
 				}
 			}
@@ -109,7 +110,9 @@ if ($action == 'set')
 	}
 
 	$activeModules = $newActiveModules;
-	dolibarr_set_const($db, 'SYSLOG_HANDLERS', json_encode($activeModules), 'chaine',0,'',0);
+
+    dolibarr_del_const($db, 'SYSLOG_HANDLERS', -1);  // To be sure ther is not a setup into another entity	
+    dolibarr_set_const($db, 'SYSLOG_HANDLERS', json_encode($activeModules), 'chaine',0,'',0);
 
 	// Check configuration
 	foreach ($activeModules as $modulename) {
@@ -194,7 +197,6 @@ print '<tr class="liste_titre">';
 print '<td>'.$langs->trans("Type").'</td><td>'.$langs->trans("Value").'</td>';
 print '<td align="right" colspan="2"><input type="submit" class="button" '.$option.' value="'.$langs->trans("Modify").'"></td>';
 print "</tr>\n";
-$var=true;
 
 foreach ($syslogModules as $moduleName)
 {
@@ -207,7 +209,7 @@ foreach ($syslogModules as $moduleName)
 	
 	print '<tr class="oddeven">';
 	print '<td width="140">';
-	print '<input '.$bc[$var].' type="checkbox" name="SYSLOG_HANDLERS[]" value="'.$moduleName.'" '.(in_array($moduleName, $activeModules) ? 'checked' : '').($moduleactive <= 0 ? 'disabled' : '').'> ';
+	print '<input class="oddeven" type="checkbox" name="SYSLOG_HANDLERS[]" value="'.$moduleName.'" '.(in_array($moduleName, $activeModules) ? 'checked' : '').($moduleactive <= 0 ? 'disabled' : '').'> ';
 	print $module->getName();
 	print '</td>';
 
@@ -217,8 +219,12 @@ foreach ($syslogModules as $moduleName)
 	{
 		foreach ($setuparray as $option)
 		{
-			if (isset($_POST[$option['constant']])) $value=$_POST[$option['constant']];
-			else if (defined($option['constant'])) $value = constant($option['constant']);
+		    $tmpoption=$option['constant'];
+		    if (! empty($tmpoption))
+		    {
+    			if (isset($_POST[$tmpoption])) $value=$_POST[$tmpoption];
+    			else if (! empty($conf->global->$tmpoption)) $value = $conf->global->$tmpoption;
+		    }
 			else $value = (isset($option['default']) ? $option['default'] : '');
 
 			print $option['name'].': <input type="text" class="flat" name="'.$option['constant'].'" value="'.$value.'"'.(isset($option['attr']) ? ' '.$option['attr'] : '').'>';

+ 5 - 5
htdocs/admin/taxes.php

@@ -136,10 +136,10 @@ else
     print '<tr class="liste_titre">';
     print '<td colspan="2">'.$langs->trans('OptionVatMode').'</td><td>'.$langs->trans('Description').'</td>';
     print "</tr>\n";
-    print '<tr '.$bc[false].'><td width="200"><input type="radio" name="tax_mode" value="0"'.($tax_mode != 1 ? ' checked' : '').'> '.$langs->trans('OptionVATDefault').'</td>';
+    print '<tr class="oddeven"><td width="200"><input type="radio" name="tax_mode" value="0"'.($tax_mode != 1 ? ' checked' : '').'> '.$langs->trans('OptionVATDefault').'</td>';
     print '<td colspan="2">'.nl2br($langs->trans('OptionVatDefaultDesc'));
     print "</td></tr>\n";
-    print '<tr '.$bc[true].'><td width="200"><input type="radio" name="tax_mode" value="1"'.($tax_mode == 1 ? ' checked' : '').'> '.$langs->trans('OptionVATDebitOption').'</td>';
+    print '<tr class="oddeven"><td width="200"><input type="radio" name="tax_mode" value="1"'.($tax_mode == 1 ? ' checked' : '').'> '.$langs->trans('OptionVATDebitOption').'</td>';
     print '<td colspan="2">'.nl2br($langs->trans('OptionVatDebitOptionDesc'))."</td></tr>\n";
 
     print "</table>\n";
@@ -152,7 +152,7 @@ else
     print '<tr class="liste_titre"><td>&nbsp;</td><td>'.$langs->trans("Buy").'</td><td>'.$langs->trans("Sell").'</td></tr>';
 
     // Products
-    print '<tr '.$bc[false].'><td>'.$langs->trans("Product").'</td>';
+    print '<tr class="oddeven"><td>'.$langs->trans("Product").'</td>';
     print '<td>';
     print $langs->trans("OnDelivery");
     print ' ('.$langs->trans("SupposedToBeInvoiceDate").')';
@@ -163,7 +163,7 @@ else
     print '</td></tr>';
 
     // Services
-    print '<tr '.$bc[true].'><td>'.$langs->trans("Services").'</td>';
+    print '<tr class="oddeven"><td>'.$langs->trans("Services").'</td>';
     print '<td>';
     if ($tax_mode == 0)
     {
@@ -206,7 +206,7 @@ foreach ($list as $key)
 {
 	
 
-	print '<tr '.$bc[$var].' class="value">';
+	print '<tr class="oddeven value">';
 
 	// Param
 	$label = $langs->trans($key); 

+ 21 - 5
htdocs/admin/translation.php

@@ -71,7 +71,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")) // All test are required to be compatible with all browsers
+if (GETPOST("button_removefilter_x") || GETPOST("button_removefilter.x") || GETPOST("button_removefilter")) // All tests are required to be compatible with all browsers
 {
     $transkey='';
     $transvalue='';
@@ -114,8 +114,16 @@ if ($action == 'add' || (GETPOST('add') && $action != 'update'))
 		}
 		else
 		{
-	        $db->rollback();
-		    setEventMessages($db->lasterror(), null, 'errors');
+
+		    $db->rollback();
+		    if ($db->lasterrno() == 'DB_ERROR_RECORD_ALREADY_EXISTS')
+		    {
+		        setEventMessages($langs->trans("WarningAnEntryAlreadyExistForTransKey"), null, 'warnings');
+		    }
+		    else 
+		    {
+		        setEventMessages($db->lasterror(), null, 'errors');
+		    }
 			$action='';
 		}
 	}
@@ -407,9 +415,17 @@ if ($mode == 'searchkey')
         print '<tr class="oddeven"><td>'.$langcode.'</td><td>'.$key.'</td><td>';
         print dol_escape_htmltag($val);
         print '</td><td align="right">';
-        if ($val != $newlangfileonly->tab_translate[$key]) 
+        if (! empty($newlangfileonly->tab_translate[$key]))
+        {
+            if ($val != $newlangfileonly->tab_translate[$key]) 
+            {
+                $htmltext = $langs->trans("OriginalValueWas", $newlangfileonly->tab_translate[$key]);
+                print $form->textwithpicto('', $htmltext, 1, 'info');
+            }
+        }
+        else
         {
-            $htmltext = $langs->trans("OriginalValueWas", $newlangfileonly->tab_translate[$key]);
+            $htmltext = $langs->trans("TransKeyWithoutOriginalValue", $key);
             print $form->textwithpicto('', $htmltext, 1, 'warning');
         }
         /*if (! empty($conf->multicompany->enabled) && !$user->entity)

+ 17 - 34
htdocs/admin/websites.php

@@ -375,11 +375,11 @@ if ($id)
             // dans les dictionnaires de donnees
             $valuetoshow=ucfirst($fieldlist[$field]);   // Par defaut
             $valuetoshow=$langs->trans($valuetoshow);   // try to translate
-            $align="left";
+            $align='';
             if ($fieldlist[$field]=='lang')            { $valuetoshow=$langs->trans("Language"); }
             if ($valuetoshow != '')
             {
-                print '<td align="'.$align.'">';
+                print '<td class="'.$align.'">';
             	if (! empty($tabhelp[$id][$value]) && preg_match('/^http(s*):/i',$tabhelp[$id][$value])) print '<a href="'.$tabhelp[$id][$value].'" target="_blank">'.$valuetoshow.' '.img_help(1,$valuetoshow).'</a>';
             	else if (! empty($tabhelp[$id][$value])) print $form->textwithpicto($valuetoshow,$tabhelp[$id][$value]);
             	else print $valuetoshow;
@@ -413,14 +413,11 @@ if ($id)
         $reshook=$hookmanager->executeHooks('createDictionaryFieldlist',$parameters, $obj, $tmpaction);    // Note that $action and $object may have been modified by some hooks
         $error=$hookmanager->error; $errors=$hookmanager->errors;
 
-        if ($id == 3) unset($fieldlist[2]);
-
         if (empty($reshook))
         {
        		fieldListWebsites($fieldlist,$obj,$tabname[$id],'add');
         }
 
-        if ($id == 4) print '<td></td>';
         print '<td colspan="3" align="right">';
         if ($action != 'edit')
         {
@@ -430,15 +427,9 @@ if ($id)
         print "</tr>";
 
         $colspan=count($fieldlist)+2;
-        if ($id == 4) $colspan++;
-
-        if (! empty($alabelisused) && $id != 25)  // If there is one label among fields, we show legend of *
-        {
-        	print '<tr><td colspan="'.$colspan.'">* '.$langs->trans("LabelUsedByDefault").'.</td></tr>';
-        }
-        print '<tr><td colspan="'.$colspan.'">&nbsp;</td></tr>';	// Keep &nbsp; to have a line with enough height
     }
 
+    print '</table>';
     print '</form>';
 
 
@@ -450,9 +441,17 @@ if ($id)
     {
         $num = $db->num_rows($resql);
         $i = 0;
-        $var=true;
         if ($num)
         {
+            print '<br>';
+            
+            print '<form action="'.$_SERVER['PHP_SELF'].'?id='.$id.'" method="POST">';
+            print '<input type="hidden" name="token" value="'.$_SESSION['newtoken'].'">';
+            print '<input type="hidden" name="page" value="'.$page.'">';
+            print '<input type="hidden" name="rowid" value="'.$rowid.'">';
+            
+            print '<table class="noborder" width="100%">';
+        
             // There is several pages
             if ($num > $listlimit)
             {
@@ -499,18 +498,12 @@ if ($id)
             // Lines with values
             while ($i < $num)
             {
-                $var = ! $var;
 
                 $obj = $db->fetch_object($resql);
                 //print_r($obj);
-                print '<tr '.$bc[$var].' id="rowid-'.$obj->rowid.'">';
+                print '<tr class="oddeven" id="rowid-'.$obj->rowid.'">';
                 if ($action == 'edit' && ($rowid == (! empty($obj->rowid)?$obj->rowid:$obj->code)))
                 {
-                    print '<form action="'.$_SERVER['PHP_SELF'].'?id='.$id.'" method="POST">';
-                    print '<input type="hidden" name="token" value="'.$_SESSION['newtoken'].'">';
-                    print '<input type="hidden" name="page" value="'.$page.'">';
-                    print '<input type="hidden" name="rowid" value="'.$rowid.'">';
-
                     $tmpaction='edit';
                     $parameters=array('fieldlist'=>$fieldlist, 'tabname'=>$tabname[$id]);
                     $reshook=$hookmanager->executeHooks('editDictionaryFieldlist',$parameters,$obj, $tmpaction);    // Note that $action and $object may have been modified by some hooks
@@ -548,16 +541,6 @@ if ($id)
 
                     $url = $_SERVER["PHP_SELF"].'?'.($page?'page='.$page.'&':'').'sortfield='.$sortfield.'&sortorder='.$sortorder.'&rowid='.(! empty($obj->rowid)?$obj->rowid:(! empty($obj->code)?$obj->code:'')).'&amp;code='.(! empty($obj->code)?urlencode($obj->code):'').'&amp;id='.$id.'&amp;';
 
-					// Favorite
-					// Only activated on country dictionary
-                    if ($id == 4)
-					{
-						print '<td align="center" class="nowrap">';
-						if ($iserasable) print '<a href="'.$url.'action='.$acts[$obj->favorite].'_favorite">'.$actl[$obj->favorite].'</a>';
-						else print $langs->trans("AlwaysActive");
-						print '</td>';
-					}
-
                     // Active
                     print '<td align="center" class="nowrap">';
                     print '<a href="'.$url.'action='.$acts[$obj->status].'">'.$actl[$obj->status].'</a>';
@@ -575,15 +558,15 @@ if ($id)
                 }
                 $i++;
             }
+            
+            print '</table>';
+            
+            print '</form>';
         }
     }
     else {
         dol_print_error($db);
     }
-
-    print '</table>';
-
-    print '</form>';
 }
 
 print '<br>';

+ 1 - 2
htdocs/admin/workflow.php

@@ -120,8 +120,7 @@ foreach($workflowcodes as $key => $params)
 		$oldfamily = $family;
    	}
 
-   	$var = !$var;
-   	print "<tr ".$bc[$var].">\n";
+   	print "<tr class=\"oddeven\">\n";
    	print "<td>".img_object('', $picto).$langs->trans('desc'.$key);
    	if (! empty($params['warning']))
    	{

+ 2 - 0
htdocs/api/class/api.class.php

@@ -90,6 +90,8 @@ class DolibarrApi
         // Remove linkedObjects. We should already have linkedObjectIds that avoid huge responses
         unset($object->linkedObjects);
         
+        unset($object->fields);
+        
         unset($object->oldline);
         
         unset($object->error);

+ 1 - 1
htdocs/api/class/api_login.class.php

@@ -102,7 +102,7 @@ class Login
 			'success' => array(
 				'code' => 200,
 				'token' => $token,
-				'message' => 'Welcome ' . $login.($reset?' - Token is new':' - This is your token (generated by a previous call)')
+				'message' => 'Welcome ' . $login.($reset?' - Token is new':' - This is your token (generated by a previous call). You can use it to make any REST API call, or enter it into the DOLAPIKEY field to use the Dolibarr API explorer.')
 			)
 		);
 	}

+ 8 - 4
htdocs/comm/card.php

@@ -1090,11 +1090,15 @@ if ($id > 0)
     
     				if (! empty($conf->commande->enabled))
     				{
-    					if (! empty($orders2invoice) && $orders2invoice > 0) print '<div class="inline-block divButAction"><a class="butAction" href="'.DOL_URL_ROOT.'/commande/orderstoinvoice.php?socid='.$object->id.'">'.$langs->trans("CreateInvoiceForThisCustomer").'</a></div>';
-    					else print '<div class="inline-block divButAction"><a class="butActionRefused" title="'.dol_escape_js($langs->trans("NoOrdersToInvoice")).'" href="#">'.$langs->trans("CreateInvoiceForThisCustomer").'</a></div>';
+    				    if ($object->client != 0 && $object->client != 2)
+    				    {
+    					   if (! empty($orders2invoice) && $orders2invoice > 0) print '<div class="inline-block divButAction"><a class="butAction" href="'.DOL_URL_ROOT.'/commande/orderstoinvoice.php?socid='.$object->id.'">'.$langs->trans("CreateInvoiceForThisCustomer").'</a></div>';
+    					   else print '<div class="inline-block divButAction"><a class="butActionRefused" title="'.dol_escape_js($langs->trans("NoOrdersToInvoice")).'" href="#">'.$langs->trans("CreateInvoiceForThisCustomer").'</a></div>';
+    				    }
+    				    else print '<div class="inline-block divButAction"><a class="butActionRefused" title="'.dol_escape_js($langs->trans("ThirdPartyMustBeEditAsCustomer")).'" href="#">'.$langs->trans("AddBill").'</a></div>';
     				}
-    
-    				if ($object->client != 0) print '<div class="inline-block divButAction"><a class="butAction" href="'.DOL_URL_ROOT.'/compta/facture/card.php?action=create&socid='.$object->id.'">'.$langs->trans("AddBill").'</a></div>';
+
+    				if ($object->client != 0 && $object->client != 2) print '<div class="inline-block divButAction"><a class="butAction" href="'.DOL_URL_ROOT.'/compta/facture/card.php?action=create&socid='.$object->id.'">'.$langs->trans("AddBill").'</a></div>';
     				else print '<div class="inline-block divButAction"><a class="butActionRefused" title="'.dol_escape_js($langs->trans("ThirdPartyMustBeEditAsCustomer")).'" href="#">'.$langs->trans("AddBill").'</a></div>';
     
     			}

+ 27 - 24
htdocs/comm/mailing/cibles.php

@@ -161,6 +161,7 @@ if ($_POST["button_removefilter"])
 	$search_lastname='';
 	$search_firstname='';
 	$search_email='';
+	$search_dest_status='';
 }
 
 
@@ -399,7 +400,7 @@ if ($object->fetch($id) >= 0)
 	if ($search_lastname)    $sql.= " AND mc.lastname    LIKE '%".$db->escape($search_lastname)."%'";
 	if ($search_firstname) $sql.= " AND mc.firstname LIKE '%".$db->escape($search_firstname)."%'";
 	if ($search_email)  $sql.= " AND mc.email  LIKE '%".$db->escape($search_email)."%'";
-	if (!empty($search_dest_status)) $sql.= " AND mc.statut=".$db->escape($search_dest_status)." ";
+	if ($search_dest_status != '' && $search_dest_status >= -1) $sql.= " AND mc.statut=".$db->escape($search_dest_status)." ";
 	$sql .= $db->order($sortfield,$sortorder);
 
 	// Count total nb of records
@@ -411,7 +412,7 @@ if ($object->fetch($id) >= 0)
 	}
 	//$nbtotalofrecords=$object->nbemail;     // nbemail is a denormalized field storing nb of targets
 	$sql .= $db->plimit($limit+1, $offset);
-
+	
 	$resql=$db->query($sql);
 	if ($resql)
 	{
@@ -446,29 +447,12 @@ if ($object->fetch($id) >= 0)
 		print '<input type="hidden" name="limit" value="'.$limit.'">';
 		
 
-		if ($page)			$param.= "&amp;page=".$page;
+		if ($page)	$param.= "&amp;page=".$page;
+		
 		print '<table class="noborder" width="100%">';
-		print '<tr class="liste_titre">';
-		print_liste_field_titre($langs->trans("EMail"),$_SERVER["PHP_SELF"],"mc.email",$param,"","",$sortfield,$sortorder);
-		print_liste_field_titre($langs->trans("Lastname"),$_SERVER["PHP_SELF"],"mc.lastname",$param,"","",$sortfield,$sortorder);
-		print_liste_field_titre($langs->trans("Firstname"),$_SERVER["PHP_SELF"],"mc.firstname",$param,"","",$sortfield,$sortorder);
-		print_liste_field_titre($langs->trans("OtherInformations"),$_SERVER["PHP_SELF"],"",$param,"","",$sortfield,$sortorder);
-		print_liste_field_titre($langs->trans("Source"),$_SERVER["PHP_SELF"],"",$param,"",'align="center"',$sortfield,$sortorder);
-		// Date sending
-		if ($object->statut < 2)
-		{
-			print_liste_field_titre('');
-		}
-		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"],"mc.statut",$param,'','align="right"',$sortfield,$sortorder);
-		print_liste_field_titre('',$_SERVER["PHP_SELF"],"",'','','',$sortfield,$sortorder,'maxwidthsearch ');
-		print '</tr>';
-
+		
 		// Ligne des champs de filtres
-		print '<tr class="liste_titre">';
+		print '<tr class="liste_titre_filter">';
 		// EMail
 		print '<td class="liste_titre">';
 		print '<input class="flat maxwidth100" type="text" name="search_email" value="'.dol_escape_htmltag($search_email).'">';
@@ -489,7 +473,7 @@ if ($object->fetch($id) >= 0)
 		print '<td class="liste_titre">';
 		print '&nbsp';
 		print '</td>';
-
+		
 		// Date sending
 		print '<td class="liste_titre">';
 		print '&nbsp';
@@ -504,6 +488,25 @@ if ($object->fetch($id) >= 0)
 		print $searchpitco;
 		print '</td>';
 		print '</tr>';
+		
+		print '<tr class="liste_titre">';
+		print_liste_field_titre($langs->trans("EMail"),$_SERVER["PHP_SELF"],"mc.email",$param,"","",$sortfield,$sortorder);
+		print_liste_field_titre($langs->trans("Lastname"),$_SERVER["PHP_SELF"],"mc.lastname",$param,"","",$sortfield,$sortorder);
+		print_liste_field_titre($langs->trans("Firstname"),$_SERVER["PHP_SELF"],"mc.firstname",$param,"","",$sortfield,$sortorder);
+		print_liste_field_titre($langs->trans("OtherInformations"),$_SERVER["PHP_SELF"],"",$param,"","",$sortfield,$sortorder);
+		print_liste_field_titre($langs->trans("Source"),$_SERVER["PHP_SELF"],"",$param,"",'align="center"',$sortfield,$sortorder);
+		// Date sending
+		if ($object->statut < 2)
+		{
+			print_liste_field_titre('');
+		}
+		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"],"mc.statut",$param,'','align="right"',$sortfield,$sortorder);
+		print_liste_field_titre('',$_SERVER["PHP_SELF"],"",'','','',$sortfield,$sortorder,'maxwidthsearch ');
+		print '</tr>';
 
 		$i = 0;
 

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

@@ -80,8 +80,8 @@ 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[0] = 'MailingStatusNotSent';
 		$this->statut_dest[1] = 'MailingStatusSent';
 		$this->statut_dest[2] = 'MailingStatusRead';
 		$this->statut_dest[3] = 'MailingStatusReadAndUnsubscribe';    // Read but ask to not be contacted anymore

+ 5 - 4
htdocs/comm/propal/class/api_proposals.class.php

@@ -260,12 +260,9 @@ class Proposals extends DolibarrApi
                         $request_data->localtax2_tx,
                         $request_data->fk_product,
                         $request_data->remise_percent,
-                        $request_data->info_bits,
-                        $request_data->fk_remise_except,
                         'HT',
                         0,
-                        $request_data->date_start,
-                        $request_data->date_end,
+                        $request_data->info_bits,
                         $request_data->product_type,
                         $request_data->rang,
                         $request_data->special_code,
@@ -273,10 +270,14 @@ class Proposals extends DolibarrApi
                         $request_data->fk_fournprice,
                         $request_data->pa_ht,
                         $request_data->label,
+                        $request_data->date_start,
+                        $request_data->date_end,
                         $request_data->array_options,
                         $request_data->fk_unit,
                         $this->element,
                         $request_data->id
+                        // not used anymore ?
+                        // $request_data->fk_remise_except
       );
 
       if ($updateRes > 0) {

+ 2 - 2
htdocs/comm/propal/contact.php

@@ -130,12 +130,12 @@ else if ($action == 'deletecontact' && $user->rights->propale->creer)
 		dol_print_error($db);
 	}
 }
-
+/*
 else if ($action == 'setaddress' && $user->rights->propale->creer)
 {
 	$result=$object->setDeliveryAddress($_POST['fk_address']);
 	if ($result < 0) dol_print_error($db,$object->error);
-}
+}*/
 
 
 /*

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

@@ -464,6 +464,7 @@ class Commande extends CommonOrder
                     if ($this->lines[$i]->fk_product > 0)
                     {
                         $mouvP = new MouvementStock($this->db);
+                        $mouvP->origin = &$this;
                         // We increment stock of product (and sub-products)
                         $result=$mouvP->reception($user, $this->lines[$i]->fk_product, $idwarehouse, $this->lines[$i]->qty, 0, $langs->trans("OrderBackToDraftInDolibarr",$this->ref));
                         if ($result < 0) { $error++; $this->error=$mouvP->error; break; }

+ 3 - 2
htdocs/commande/contact.php

@@ -107,13 +107,14 @@ else if ($action == 'deletecontact' && $user->rights->commande->creer)
 		dol_print_error($db);
 	}
 }
-
+/*
 else if ($action == 'setaddress' && $user->rights->commande->creer)
 {
 	$object->fetch($id);
 	$result=$object->setDeliveryAddress($_POST['fk_address']);
 	if ($result < 0) dol_print_error($db,$object->error);
-}
+}*/
+
 
 /*
  * View

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

@@ -117,7 +117,7 @@ if ($action == 'add')
     
     if ($conf->global->MAIN_BANK_ACCOUNTANCY_CODE_ALWAYS_REQUIRED && empty($object->account_number))
     {
-        setEventMessages($langs->transnoentitiesnoconv("ErrorFieldRequired",$langs->transnoentitiesnoconv("AccountancyCode")), null, 'error');
+        setEventMessages($langs->transnoentitiesnoconv("ErrorFieldRequired",$langs->transnoentitiesnoconv("AccountancyCode")), null, 'errors');
         $action='create';       // Force chargement page en mode creation
         $error++;
     }

+ 1 - 1
htdocs/compta/facture/admin/facture_cust_extrafields.php

@@ -70,7 +70,7 @@ print load_fiche_titre($langs->trans("BillsSetup"),$linkback,'title_setup');
 
 $head = invoice_admin_prepare_head();
 
-dol_fiche_head($head, 'attributes', $langs->trans("Invoices"), 0, 'invoice');
+dol_fiche_head($head, 'attributes', $langs->trans("Invoices"), -1, 'invoice');
 
 require DOL_DOCUMENT_ROOT.'/core/tpl/admin_extrafields_view.tpl.php';
 

+ 1 - 1
htdocs/compta/facture/admin/facturedet_cust_extrafields.php

@@ -71,7 +71,7 @@ print load_fiche_titre($langs->trans("BillsSetup"),$linkback,'title_setup');
 
 $head = invoice_admin_prepare_head();
 
-dol_fiche_head($head, 'attributeslines', $langs->trans("Invoices"), 0, 'invoice');
+dol_fiche_head($head, 'attributeslines', $langs->trans("Invoices"), -1, 'invoice');
 
 require DOL_DOCUMENT_ROOT.'/core/tpl/admin_extrafields_view.tpl.php';
 

+ 11 - 22
htdocs/compta/facture/card.php

@@ -611,7 +611,9 @@ if (empty($reshook))
 		{
 			$db->begin();
 
-			// Boucle sur chaque taux de tva
+			$amount_ht = $amount_tva = $amount_ttc = array();
+			
+			// Loop on each vat rate
 			$i = 0;
 			foreach ($object->lines as $line)
 			{
@@ -630,20 +632,19 @@ if (empty($reshook))
 				$discount->description = '(CREDIT_NOTE)';
 			elseif ($object->type == Facture::TYPE_DEPOSIT)
 				$discount->description = '(DEPOSIT)';
-			elseif ($object->type == Facture::TYPE_STANDARD)
+			elseif ($object->type == Facture::TYPE_STANDARD || $object->type == Facture::TYPE_REPLACEMENT || $object->type == Facture::TYPE_SITUATION)
 				$discount->description = '(EXCESS RECEIVED)';
 			else {
 				setEventMessages($langs->trans('CantConvertToReducAnInvoiceOfThisType'), null, 'errors');
 			}
-			$discount->tva_tx = abs($object->total_ttc);
 			$discount->fk_soc = $object->socid;
 			$discount->fk_facture_source = $object->id;
 
 			$error = 0;
 			
-			if ($object->type == Facture::TYPE_STANDARD) {
-				
-				// If we're on a standard invoice, we have to get excess received to create it in TTC wuthout VAT
+			if ($object->type == Facture::TYPE_STANDARD || $object->type == Facture::TYPE_REPLACEMENT || $object->type == Facture::TYPE_SITUATION) 
+			{
+				// If we're on a standard invoice, we have to get excess received to create a discount in TTC without VAT
 				
 				$sql = 'SELECT SUM(pf.amount) as total_paiements
 						FROM llx_c_paiement as c, llx_paiement_facture as pf, llx_paiement as p
@@ -663,8 +664,9 @@ if (empty($reshook))
 					$error++;
 				}
 				
-			} else {
-			
+			}
+			if ($object->type == Facture::TYPE_CREDIT_NOTE || $object->type == Facture::TYPE_DEPOSIT)
+			{
 				foreach ($amount_ht as $tva_tx => $xxx)
 				{
 					$discount->amount_ht = abs($amount_ht[$tva_tx]);
@@ -871,19 +873,6 @@ if (empty($reshook))
 	                    $object->addline($langs->trans('invoiceAvoirLineWithPaymentRestAmount'),$remain_to_pay,1,0,0,0,0,0,'','','TTC');
 	                }
 	            }
-
-				// Add predefined lines
-				/*
-	             TODO delete
-	             for($i = 1; $i <= $NBLINES; $i ++) {
-					if ($_POST['idprod' . $i]) {
-						$product = new Product($db);
-						$product->fetch($_POST['idprod' . $i]);
-						$startday = dol_mktime(12, 0, 0, $_POST['date_start' . $i . 'month'], $_POST['date_start' . $i . 'day'], $_POST['date_start' . $i . 'year']);
-						$endday = dol_mktime(12, 0, 0, $_POST['date_end' . $i . 'month'], $_POST['date_end' . $i . 'day'], $_POST['date_end' . $i . 'year']);
-						$result = $object->addline($product->description, $product->price, $_POST['qty' . $i], $product->tva_tx, $product->localtax1_tx, $product->localtax2_tx, $_POST['idprod' . $i], $_POST['remise_percent' . $i], $startday, $endday, 0, 0, '', $product->price_base_type, $product->price_ttc, $product->type);
-					}
-				}*/
 			}
 		}
 
@@ -2191,7 +2180,7 @@ if ($action == 'create')
 	else
 	{
 		print '<td colspan="2">';
-		print $form->select_company($soc->id, 'socid', '(s.client = 1 OR s.client = 3) AND status=1', 'SelectThirdParty');
+		print $form->select_company($soc->id, 'socid', '(s.client = 1 OR s.client = 3) AND status=1', 'SelectThirdParty', 0, 0, null, 0, 'minwidth300');
 		// Option to reload page to retrieve customer informations. Note, this clear other input
 		if (!empty($conf->global->RELOAD_PAGE_ON_CUSTOMER_CHANGE))
 		{

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

@@ -35,8 +35,9 @@ require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
 require_once DOL_DOCUMENT_ROOT.'/core/class/html.formother.class.php';
 if (! empty($conf->projet->enabled)) {
     require_once DOL_DOCUMENT_ROOT . '/projet/class/project.class.php';
-    require_once DOL_DOCUMENT_ROOT . '/core/class/html.formprojet.class.php';
+    //require_once DOL_DOCUMENT_ROOT . '/core/class/html.formprojet.class.php';
 }
+require_once DOL_DOCUMENT_ROOT . '/core/class/html.formprojet.class.php';
 require_once DOL_DOCUMENT_ROOT . '/core/class/doleditor.class.php';
 require_once DOL_DOCUMENT_ROOT . '/core/lib/invoice.lib.php';
 

+ 19 - 19
htdocs/compta/paiement.php

@@ -1,6 +1,6 @@
 <?php
 /* Copyright (C) 2001-2006 Rodolphe Quiedeville  <rodolphe@quiedeville.org>
- * Copyright (C) 2004-2016 Laurent Destailleur   <eldy@users.sourceforge.net>
+ * Copyright (C) 2004-2017 Laurent Destailleur   <eldy@users.sourceforge.net>
  * Copyright (C) 2005      Marc Barilley / Ocebo <marc@ocebo.com>
  * Copyright (C) 2005-2012 Regis Houssin         <regis.houssin@capnetworks.com>
  * Copyright (C) 2007      Franky Van Liedekerke <franky.van.liedekerke@telenet.be>
@@ -221,7 +221,7 @@ if (empty($reshook))
 	    $db->begin();
 
 	    // Clean parameters amount if payment is for a credit note
-	    if (GETPOST('type') == 2)
+	    if (GETPOST('type') == Facture::TYPE_CREDIT_NOTE)
 	    {
 		    foreach ($amounts as $key => $value)	// How payment is dispatch
 		    {
@@ -249,7 +249,7 @@ if (empty($reshook))
 	    // Creation of payment line
 	    $paiement = new Paiement($db);
 	    $paiement->datepaye     = $datepaye;
-	    $paiement->amounts      = $amounts;   // Array with all payments dispatching
+	    $paiement->amounts      = $amounts;   // Array with all payments dispatching with invoice id
 	    $paiement->multicurrency_amounts = $multicurrency_amounts;   // Array with all payments dispatching
 	    $paiement->paiementid   = dol_getIdFromCode($db,GETPOST('paiementcode'),'c_paiement');
 	    $paiement->num_paiement = GETPOST('num_paiement');
@@ -257,7 +257,7 @@ if (empty($reshook))
 
 	    if (! $error)
 	    {
-	    	$paiement_id = $paiement->create($user, (GETPOST('closepaidinvoices')=='on'?1:0));
+	    	$paiement_id = $paiement->create($user, (GETPOST('closepaidinvoices')=='on'?1:0));    // This include closing invoices
 	    	if ($paiement_id < 0)
 	        {
 	            setEventMessages($paiement->error, $paiement->errors, 'errors');
@@ -268,7 +268,7 @@ if (empty($reshook))
 	    if (! $error)
 	    {
 	    	$label='(CustomerInvoicePayment)';
-	    	if (GETPOST('type') == 2) $label='(CustomerInvoicePaymentBack)';
+	    	if (GETPOST('type') == Facture::TYPE_CREDIT_NOTE) $label='(CustomerInvoicePaymentBack)';  // Refund of a credit note
 	        $result=$paiement->addPaymentToBank($user,'payment',$label,GETPOST('accountid'),GETPOST('chqemetteur'),GETPOST('chqbank'));
 	        if ($result < 0)
 	        {
@@ -281,7 +281,7 @@ if (empty($reshook))
 	    {
 	        $db->commit();
 
-	        // If payment dispatching on more than one invoice, we keep on summary page, otherwise go on invoice card
+	        // If payment dispatching on more than one invoice, we keep on summary page, otherwise jump on invoice card
 	        $invoiceid=0;
 	        foreach ($paiement->amounts as $key => $amount)
 	        {
@@ -309,7 +309,7 @@ if (empty($reshook))
  * View
  */
 
-llxHeader();
+llxHeader('', $langs->trans("Payment"));
 
 $form=new Form($db);
 
@@ -324,8 +324,8 @@ if ($action == 'create' || $action == 'confirm_paiement' || $action == 'add_paie
 		$facture->fetch_thirdparty();
 
 		$title='';
-		if ($facture->type != 2) $title.=$langs->trans("EnterPaymentReceivedFromCustomer");
-		if ($facture->type == 2) $title.=$langs->trans("EnterPaymentDueToCustomer");
+		if ($facture->type != Facture::TYPE_CREDIT_NOTE) $title.=$langs->trans("EnterPaymentReceivedFromCustomer");
+		if ($facture->type == Facture::TYPE_CREDIT_NOTE) $title.=$langs->trans("EnterPaymentDueToCustomer");
 		print load_fiche_titre($title);
 
 		// Initialize data for confirmation (this is used because data can be change during confirmation)
@@ -347,7 +347,7 @@ if ($action == 'create' || $action == 'confirm_paiement' || $action == 'add_paie
 		}
 
 		// Add realtime total information
-		if ($conf->use_javascript_ajax)
+		if (! empty($conf->use_javascript_ajax))
 		{
 			print "\n".'<script type="text/javascript" language="javascript">';
 			print '$(document).ready(function () {
@@ -369,7 +369,7 @@ if ($action == 'create' || $action == 'confirm_paiement' || $action == 'add_paie
 			                    }
             					if ($(\'#fieldchqemetteur\').val() == \'\')
             					{
-            						var emetteur = ('.$facture->type.' == 2) ? \''.dol_escape_js(dol_escape_htmltag($conf->global->MAIN_INFO_SOCIETE_NOM)).'\' : jQuery(\'#thirdpartylabel\').val();
+            						var emetteur = ('.$facture->type.' == '.Facture::TYPE_CREDIT_NOTE.') ? \''.dol_escape_js(dol_escape_htmltag($conf->global->MAIN_INFO_SOCIETE_NOM)).'\' : jQuery(\'#thirdpartylabel\').val();
             						$(\'#fieldchqemetteur\').val(emetteur);
             					}
             				}
@@ -437,14 +437,14 @@ if ($action == 'create' || $action == 'confirm_paiement' || $action == 'add_paie
 			';
 
 			print '	});'."\n";
-			if (!empty($conf->use_javascript_ajax)){
-				//Add js for AutoFill
-				print ' $(document).ready(function () {';
-				print ' 	$(".AutoFillAmout").on(\'click touchstart\', function(){
-								$("input[name="+$(this).data(\'rowname\')+"]").val($(this).data("value")).trigger("change");
-							});';
-				print '	});'."\n";
-			}
+
+			//Add js for AutoFill
+			print ' $(document).ready(function () {';
+			print ' 	$(".AutoFillAmout").on(\'click touchstart\', function(){
+							$("input[name="+$(this).data(\'rowname\')+"]").val($(this).data("value")).trigger("change");
+						});';
+			print '	});'."\n";
+
 			print '	</script>'."\n";
 		}
 

+ 55 - 5
htdocs/compta/paiement/class/paiement.class.php

@@ -255,7 +255,7 @@ class Paiement extends CommonObject
                                 {
                                     if (! empty($conf->prelevement->enabled))
                                     {
-                                        // TODO Check if this payment has a withdraw request
+                                        // FIXME Check if this invoice has a withdraw request
                                         // if not, $mustwait++;      // This will disable automatic close on invoice to allow to process
                                     }
                                 }
@@ -275,11 +275,61 @@ class Paiement extends CommonObject
                             else if ($mustwait) dol_syslog("There is ".$mustwait." differed payment to process, we do nothing more.");
                             else
                             {
-                                $result=$invoice->set_paid($user,'','');
-                                if ($result<0)
+                                // If invoice is a down payment, we also convert down payment to discount
+                                if ($invoice->type == Facture::TYPE_DEPOSIT)
                                 {
-                                    $this->error=$invoice->error;
-                                    $error++;
+			                        $amount_ht = $amount_tva = $amount_ttc = array();
+			                         
+                                    // Loop on each vat rate
+                                    $i = 0;
+                                    foreach ($invoice->lines as $line)
+                                    {
+                                        if ($line->total_ht!=0)
+                                        { 	// no need to create discount if amount is null
+                                            $amount_ht[$line->tva_tx] += $line->total_ht;
+                                            $amount_tva[$line->tva_tx] += $line->total_tva;
+                                            $amount_ttc[$line->tva_tx] += $line->total_ttc;
+                                            $i ++;
+                                        }
+                                    }
+                                    
+                                    // Insert one discount by VAT rate category
+                                    $discount = new DiscountAbsolute($this->db);
+                                    $discount->description = '(DEPOSIT)';
+                                    $discount->fk_soc = $invoice->socid;
+                                    $discount->fk_facture_source = $invoice->id;
+                                
+                                    foreach ($amount_ht as $tva_tx => $xxx)
+                                    {
+                                        $discount->amount_ht = abs($amount_ht[$tva_tx]);
+                                        $discount->amount_tva = abs($amount_tva[$tva_tx]);
+                                        $discount->amount_ttc = abs($amount_ttc[$tva_tx]);
+                                        $discount->tva_tx = abs($tva_tx);
+                            
+                                        $result = $discount->create($user);
+                                        if ($result < 0)
+                                        {
+                                            $error++;
+                                            break;
+                                        }
+                                    }
+                                    
+                                    if ($error)
+                                    {
+                                        setEventMessages($discount->error, $discount->errors, 'errors');
+                                        $error++;
+                                    }                                   
+                                }
+                                
+                                // Set invoice to paid
+                                if (! $error)
+                                {
+                                    $result=$invoice->set_paid($user,'','');
+                                    if ($result<0)
+                                    {
+                                        $this->error=$invoice->error;
+                                        $error++;
+                                    }
                                 }
                             }
 					    }

+ 6 - 1
htdocs/core/ajax/check_notifications.php

@@ -21,7 +21,6 @@ if (! defined('NOREQUIREMENU'))  define('NOREQUIREMENU','1');
 if (! defined('NOREQUIREHTML'))  define('NOREQUIREHTML','1');
 if (! defined('NOREQUIREAJAX'))  define('NOREQUIREAJAX','1');
 if (! defined('NOREQUIRESOC'))   define('NOREQUIRESOC','1');
-if (! defined('NOREQUIRETRAN'))  define('NOREQUIRETRAN','1');
 
 require '../../main.inc.php';
 
@@ -88,9 +87,11 @@ if ($time >= $_SESSION['auto_check_events_not_before'])
         while ($obj = $db->fetch_object($resql)) 
         {
             $langs->load("agenda");
+            $langs->load("commercial");
             
             $actionmod->fetch($obj->id);
 
+            // Message must be formated and translated to be used with javascript directly
             $event = array();
             $event['type'] = 'agenda';
             $event['id'] = $actionmod->id;
@@ -101,6 +102,10 @@ if ($time >= $_SESSION['auto_check_events_not_before'])
             $eventfound[] = $event;
         }
     }
+    else
+    {
+        dol_syslog("Error sql = ".$db->lasterror(), LOG_ERR);
+    }
 
 }
 

+ 5 - 2
htdocs/core/boxes/box_external_rss.php

@@ -80,8 +80,11 @@ class box_external_rss extends ModeleBoxes
 		// documents/externalrss is created by module activation
 		// documents/externalrss/tmp is created by rssparser
 
+		$keyforparamurl="EXTERNAL_RSS_URLRSS_".$site;
+		$keyforparamtitle="EXTERNAL_RSS_TITLE_".$site;
+		
 		// Get RSS feed
-        $url=@constant("EXTERNAL_RSS_URLRSS_".$site);
+		$url=$conf->global->$keyforparamurl;
 
         $rssparser=new RssParser($this->db);
 		$result = $rssparser->parser($url, $this->max, $cachedelay, $conf->externalrss->dir_temp);
@@ -90,7 +93,7 @@ class box_external_rss extends ModeleBoxes
 		$description=$rssparser->getDescription();
 		$link=$rssparser->getLink();
 
-        $title=$langs->trans("BoxTitleLastRssInfos",$max, @constant("EXTERNAL_RSS_TITLE_". $site));
+        $title=$langs->trans("BoxTitleLastRssInfos", $max, $conf->global->$keyforparamtitle);
         if ($result < 0 || ! empty($rssparser->error))
         {
             // Show warning

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

@@ -51,7 +51,8 @@ abstract class CommonInvoice extends CommonObject
     const TYPE_DEPOSIT = 3;
 
     /**
-     * Proforma invoice
+     * Proforma invoice. 
+     * @deprectad Remove this. A "proforma invoice" is an order with a look of invoice, not an invoice !
      */
     const TYPE_PROFORMA = 4;
 

+ 367 - 56
htdocs/core/class/commonobject.class.php

@@ -11,6 +11,7 @@
  * Copyright (C) 2012      Cedric Salvador      <csalvador@gpcsolutions.fr>
  * Copyright (C) 2015      Alexandre Spangaro   <aspangaro.dolibarr@gmail.com>
  * Copyright (C) 2016      Bahfir abbes         <dolipar@dolipar.org>
+ * Copyright (C) 2017      ATM Consulting       <support@atm-consulting.fr>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -238,6 +239,7 @@ abstract class CommonObject
 
 	/**
 	 * @var int Delivery address ID
+	 * @deprecated
 	 * @see setDeliveryAddress()
 	 */
 	public $fk_delivery_address;
@@ -1623,7 +1625,8 @@ abstract class CommonObject
 
     /**
      *	Define delivery address
-     *
+     *  @deprecated
+     *  
      *	@param      int		$id		Address id
      *	@return     int				<0 si ko, >0 si ok
      */
@@ -3223,51 +3226,14 @@ abstract class CommonObject
 
     /**
      *  Return if a country is inside the EEC (European Economic Community)
-     *  TODO Add a field into dictionary
+     *  @deprecated
      *
      *  @return     boolean		true = country inside EEC, false = country outside EEC
      */
     function isInEEC()
     {
-        // List of all country codes that are in europe for european vat rules
-        // List found on http://ec.europa.eu/taxation_customs/common/faq/faq_1179_en.htm#9
-        $country_code_in_EEC=array(
-    			'AT',	// Austria
-    			'BE',	// Belgium
-    			'BG',	// Bulgaria
-    			'CY',	// Cyprus
-    			'CZ',	// Czech republic
-    			'DE',	// Germany
-    			'DK',	// Danemark
-    			'EE',	// Estonia
-    			'ES',	// Spain
-    			'FI',	// Finland
-    			'FR',	// France
-    			'GB',	// United Kingdom
-    			'GR',	// Greece
-    			'HR',   // Croatia
-                'NL',	// Holland
-    			'HU',	// Hungary
-    			'IE',	// Ireland
-    			'IM',	// Isle of Man - Included in UK
-    			'IT',	// Italy
-    			'LT',	// Lithuania
-    			'LU',	// Luxembourg
-    			'LV',	// Latvia
-    			'MC',	// Monaco - Included in France
-    			'MT',	// Malta
-                //'NO',	// Norway
-    			'PL',	// Poland
-    			'PT',	// Portugal
-    			'RO',	// Romania
-    			'SE',	// Sweden
-    			'SK',	// Slovakia
-    			'SI',	// Slovenia
-    			'UK',	// United Kingdom
-        //'CH',	// Switzerland - No. Swizerland in not in EEC
-        );
-        //print "dd".$this->country_code;
-        return in_array($this->country_code,$country_code_in_EEC);
+        require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
+        return isInEEC($this);
     }
 
 
@@ -3754,21 +3720,6 @@ abstract class CommonObject
     }
 
 
-	/**
-	 * Show the array with all margin infos
-	 *
-	 * @param 		bool	$force_price	Force price
-	 * @return		void
-	 * @deprecated	3.8 Load FormMargin class and make a direct call to displayMarginInfos
-	 */
-	function displayMarginInfos($force_price=false)
-	{
-		include_once DOL_DOCUMENT_ROOT.'/core/class/html.formmargin.class.php';
-		$formmargin=new FormMargin($this->db);
-		$formmargin->displayMarginInfos($this, $force_price);
-	}
-
-
 	/**
 	 *	Add resources to the current object : add entry into llx_element_resources
 	 *	Need $this->element & $this->id
@@ -4711,4 +4662,364 @@ abstract class CommonObject
 		}
 		return $buyPrice;
 	}
+	
+	/**
+	 * Function test if type is date
+	 *
+	 * @param   array   $info   content informations of field
+	 * @return                  bool
+	 */
+	protected function isDate($info)
+	{
+		if(isset($info['type']) && $info['type']=='date') return true;
+		else return false;
+	}
+	
+	/**
+	 * Function test if type is array
+	 *
+	 * @param   array   $info   content informations of field
+	 * @return                  bool
+	 */
+	protected function isArray($info)
+	{
+		if(is_array($info))
+		{
+			if(isset($info['type']) && $info['type']=='array') return true;
+			else return false;
+		}
+		else return false;
+	}
+	
+	/**
+	 * Function test if type is null
+	 *
+	 * @param   array   $info   content informations of field
+	 * @return                  bool
+	 */
+	protected function isNull($info)
+	{
+		if(is_array($info))
+		{
+			if(isset($info['type']) && $info['type']=='null') return true;
+			else return false;
+		}
+		else return false;
+	}
+	
+	/**
+	 * Function test if type is integer
+	 *
+	 * @param   array   $info   content informations of field
+	 * @return                  bool
+	 */
+	protected function isInt($info)
+	{
+		if(is_array($info))
+		{
+			if(isset($info['type']) && ($info['type']=='int' || $info['type']=='integer' )) return true;
+			else return false;
+		}
+		else return false;
+	}
+	
+	/**
+	 * Function test if type is float
+	 *
+	 * @param   array   $info   content informations of field
+	 * @return                  bool
+	 */
+	protected function isFloat($info)
+	{
+		if(is_array($info))
+		{
+			if(isset($info['type']) && $info['type']=='float') return true;
+			else return false;
+		}
+		else return false;
+	}
+	
+	/**
+	 * Function test if type is text
+	 *
+	 * @param   array   $info   content informations of field
+	 * @return                  bool
+	 */
+	protected function isText($info)
+	{
+		if(is_array($info))
+		{
+			if(isset($info['type']) && $info['type']=='text') return true;
+			else return false;
+		}
+		else return false;
+	}
+	
+	/**
+	 * Function test if is indexed
+	 *
+	 * @param   array   $info   content informations of field
+	 * @return                  bool
+	 */
+	protected function isIndex($info)
+	{
+		if(is_array($info))
+		{
+			if(isset($info['index']) && $info['index']==true) return true;
+			else return false;
+		}
+		else return false;
+	}
+	
+	/**
+	 * Function to prepare the values to insert
+	 *
+	 * @return array
+	 */
+	private function set_save_query()
+	{
+		$query=array();
+		foreach ($this->fields as $field=>$info)
+		{
+			if($this->isDate($info))
+			{
+				if(empty($this->{$field}))
+				{
+					$query[$field] = NULL;
+				}
+				else
+				{
+					$query[$field] = $this->db->idate($this->{$field});
+				}
+			}
+			else if($this->isArray($info))
+			{
+				$query[$field] = serialize($this->{$field});
+			}
+			else if($this->isInt($info))
+			{
+				$query[$field] = (int) price2num($this->{$field});
+			}
+			else if($this->isFloat($info))
+			{
+				$query[$field] = (double) price2num($this->{$field});
+			}
+			elseif($this->isNull($info))
+			{
+				$query[$field] = (is_null($this->{$field}) || (empty($this->{$field}) && $this->{$field}!==0 && $this->{$field}!=='0') ? null : $this->{$field});
+			}
+			else
+			{
+				$query[$field] = $this->{$field};
+			}
+		}
+		
+		return $query;
+	}
+
+	/**
+	 * Create object into database
+	 *
+	 * @param  User $user      User that creates
+	 * @param  bool $notrigger false=launch triggers after, true=disable triggers
+	 *
+	 * @return int <0 if KO, Id of created object if OK
+	 */
+	public function createCommon(User $user, $notrigger = false)
+	{
+	    
+	    $fields = array_merge(array('datec'=>$this->db->idate(dol_now())), $this->set_save_query());
+	    
+	    foreach ($fields as $k => $v) {
+	    	
+	    	$keys[] = $k;
+	    	$values[] = $this->quote($v);
+	    	
+	    }
+	    $sql = 'INSERT INTO '.MAIN_DB_PREFIX.$this->table_element.'
+					( '.implode( ",", $keys ).' )
+					VALUES ( '.implode( ",", $values ).' ) ';
+	    $res = $this->db->query( $sql );
+	    if($res===false) {
+	    	
+	    	return false;
+	    }
+	    
+	    // TODO Add triggers
+	    
+	    return true;
+	    
+	}
+	
+	/**
+	 * Function to load data into current object this
+	 *
+	 * @param   stdClass    $obj    Contain data of object from database
+	 */
+	private function set_vars_by_db(&$obj)
+	{
+		foreach ($this->fields as $field => $info)
+		{
+			if($this->isDate($info))
+			{
+				if(empty($obj->{$field}) || $obj->{$field} === '0000-00-00 00:00:00' || $obj->{$field} === '1000-01-01 00:00:00') $this->{$field} = 0;
+				else $this->{$field} = strtotime($obj->{$field});
+			}
+			elseif($this->isArray($info))
+			{
+				$this->{$field} = @unserialize($obj->{$field});
+				// Hack for data not in UTF8
+				if($this->{$field } === FALSE) @unserialize(utf8_decode($obj->{$field}));
+			}
+			elseif($this->isInt($info))
+			{
+				$this->{$field} = (int) $obj->{$field};
+			}
+			elseif($this->isFloat($info))
+			{
+				$this->{$field} = (double) $obj->{$field};
+			}
+			elseif($this->isNull($info))
+			{
+				$val = $obj->{$field};
+				// zero is not null
+				$this->{$field} = (is_null($val) || (empty($val) && $val!==0 && $val!=='0') ? null : $val);
+			}
+			else
+			{
+				$this->{$field} = $obj->{$field};
+			}
+			
+		}
+	}
+	
+	/**
+	 * Function to concat keys of fields
+	 *
+	 * @return string
+	 */
+	private function get_field_list()
+	{
+		$keys = array_keys($this->fields);
+		return implode(',', $keys);
+	}
+	
+	/**
+	 * Load object in memory from the database
+	 *
+	 * @param int    $id  Id object
+	 * @param string $ref Ref
+	 *
+	 * @return int <0 if KO, 0 if not found, >0 if OK
+	 */
+	public function fetchCommon($id, $ref = null)
+	{
+	    
+		if (empty($id) && empty($ref)) return false;
+		
+		$sql = 'SELECT '.$this->get_field_list().', datec, tms';
+		$sql.= ' FROM '.MAIN_DB_PREFIX.$this->table_element;
+		
+		if(!empty($id)) $sql.= ' WHERE rowid = '.$id;
+		else  $sql.= ' WHERE ref = \''.$this->quote($ref).'\'';
+		
+		$res = $this->db->query($sql);
+		if($obj = $this->db->fetch_object($res))
+		{
+			$this->id = $id;
+			$this->set_vars_by_db($obj);
+			
+			$this->datec = $this->db->idate($obj->datec);
+			$this->tms = $this->db->idate($obj->tms);
+			
+			return $this->id;
+		}
+		else
+		{
+			$this->error = $this->db->lasterror();
+			$this->errors[] = $this->error;
+			return -1;
+		}
+		
+	}
+
+	/**
+	 * Update object into database
+	 *
+	 * @param  User $user      User that modifies
+	 * @param  bool $notrigger false=launch triggers after, true=disable triggers
+	 *
+	 * @return int <0 if KO, >0 if OK
+	 */
+	public function updateCommon(User $user, $notrigger = false)
+	{
+		$fields = $this->set_save_query();
+		
+		foreach ($fields as $k => $v) {
+			
+			if (is_array($key)){
+				$i=array_search($k, $key);
+				if ( $i !== false) {
+					$where[] = $key[$i].'=' . $this->quote( $v ) ;
+					continue;
+				}
+			} else {
+				if ( $k == $key) {
+					$where[] = $k.'=' .$this->quote( $v ) ;
+					continue;
+				}
+			}
+			
+			$tmp[] = $k.'='.$this->quote($v);
+		}
+		$sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element.' SET '.implode( ',', $tmp ).' WHERE rowid='.$this->id ;
+		$res = $this->db->query( $sql );
+		
+		if($res===false) {
+			//error
+			return false;
+		}
+		
+		// TODO Add triggers
+		
+		return true;
+	}
+	
+	/**
+	 * Delete object in database
+	 *
+	 * @param User $user      User that deletes
+	 * @param bool $notrigger false=launch triggers after, true=disable triggers
+	 *
+	 * @return int <0 if KO, >0 if OK
+	 */
+	public function deleteCommon(User $user, $notrigger = false)
+	{
+		$sql = 'DELETE FROM '.MAIN_DB_PREFIX.$this->table_element.' WHERE rowid='.$this->id;
+		
+		$res = $this->db->query( $sql );
+		if($res===false) {
+			return false;
+		}
+		
+		// TODO Add triggers
+		
+		return true;
+	}
+
+	/**
+	 * Add quote to field value if necessary
+	 *
+	 * @param string|int	$value	value to protect
+	 * @return string|int
+	 */
+	protected function quote($value) {
+	
+	    if(is_null($value)) return 'NULL';
+	    else if(is_numeric($value)) return $value;
+	    else return "'".$this->db->escape( $value )."'";
+	
+	}
+	
 }
+

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

@@ -210,7 +210,7 @@ class Conf
                 $file=dol_sanitizeFileName($file);
                 include_once DOL_DOCUMENT_ROOT . "/".$file."/".$file."_consts.php";
                 foreach ($file2bddconsts as $key=>$value) {
-                    $this->global->$key=constant($value);
+                    $this->global->$key=$value;
                 }
             }
         }
@@ -358,8 +358,8 @@ class Conf
     			$this->supplier_order->dir_temp=$rootfordata."/fournisseur/commande/temp";
     			$this->supplier_invoice=new stdClass();
     			$this->supplier_invoice->enabled=1;
-    			$this->supplier_order->dir_output=$rootfordata."/fournisseur/facture";
-    			$this->supplier_order->dir_temp=$rootfordata."/fournisseur/facture/temp";
+    			$this->supplier_invoice->dir_output=$rootfordata."/fournisseur/facture";
+    			$this->supplier_invoice->dir_temp=$rootfordata."/fournisseur/facture/temp";
 			}
 		}
 

+ 440 - 0
htdocs/core/class/coreobject.class.php

@@ -0,0 +1,440 @@
+<?php
+/* EXPERIMENTAL
+ * 
+ * Copyright (C) 2016		ATM Consulting			<support@atm-consulting.fr>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 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/>.
+ */
+
+/**
+ *	\file       htdocs/core/class/coreobject.class.php
+ *	\ingroup    core
+ *	\brief      File of class to manage all object. Might be replace or merge into commonobject
+ */
+ 
+require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php';
+
+class CoreObject extends CommonObject
+{
+	public $withChild = true;
+
+	/**
+	 *  @var Array $_fields Fields to synchronize with Database
+	 */
+	protected $fields=array();
+
+    /**
+	 *  Constructor
+	 *
+	 *  @param      DoliDB		$db      Database handler
+	 */
+	function __construct(DoliDB &$db)
+    {
+        $this->db = $db;
+	}
+
+    /**
+     * Function to init fields
+     *
+     * @return bool
+     */
+	protected function init()
+    {
+		$this->id = 0;
+		$this->datec = 0;
+		$this->tms = 0;
+		
+		if (!empty($this->fields))
+		{
+			foreach ($this->fields as $field=>$info)
+			{
+		        if ($this->isDate($info)) $this->{$field} = time();
+		        elseif ($this->isArray($info)) $this->{$field} = array();
+		        elseif ($this->isInt($info)) $this->{$field} = (int) 0;
+		        elseif ($this->isFloat($info)) $this->{$field} = (double) 0;
+				else $this->{$field} = '';
+		    }
+
+            $this->to_delete=false;
+            $this->is_clone=false;
+			
+			return true;
+		}
+		else
+        {
+			return false;
+		}
+			
+	}
+
+    /**
+     * Test type of field
+     *
+     * @param   string  $field  name of field
+     * @param   string  $type   type of field to test
+     * @return                  value of field or false
+     */
+    private function checkFieldType($field, $type)
+    {
+		if (isset($this->fields[$field]) && method_exists($this, 'is_'.$type))
+		{
+			return $this->{'is_'.$type}($this->fields[$field]);
+		}
+		else
+        {
+            return false;
+        }
+	}
+
+    /**
+     *	Get object and children from database
+     *
+     *	@param      int			$id       		Id of object to load
+     * 	@param		bool		$loadChild		used to load children from database
+     *	@return     int         				>0 if OK, <0 if KO, 0 if not found
+     */
+	public function fetch($id, $loadChild = true)
+    {
+		
+    	$res = $this->fetchCommon($id);
+    	if($res>0) {
+    		if ($loadChild) $this->fetchChild();
+    	}
+    	
+    	return $res;
+		
+	}
+
+
+    /**
+     * Function to instantiate a new child
+     *
+     * @param   string  $tabName        Table name of child
+     * @param   int     $id             If id is given, we try to return his key if exist or load if we try_to_load
+     * @param   string  $key            Attribute name of the object id
+     * @param   bool    $try_to_load    Force the fetch if an id is given
+     * @return                          int
+     */
+    public function addChild($tabName, $id=0, $key='id', $try_to_load = false)
+    {
+		if(!empty($id))
+		{
+			foreach($this->{$tabName} as $k=>&$object)
+			{
+				if($object->{$key} === $id) return $k;
+			}
+		}
+	
+		$k = count($this->{$tabName});
+	
+		$className = ucfirst($tabName);
+		$this->{$tabName}[$k] = new $className($this->db);
+		if($id>0 && $key==='id' && $try_to_load)
+		{
+			$this->{$tabName}[$k]->fetch($id); 
+		}
+
+		return $k;
+	}
+
+
+    /**
+     * Function to set a child as to delete
+     *
+     * @param   string  $tabName        Table name of child
+     * @param   int     $id             Id of child to set as to delete
+     * @param   string  $key            Attribute name of the object id
+     * @return                          bool
+     */
+    public function removeChild($tabName, $id, $key='id')
+    {
+		foreach ($this->{$tabName} as &$object)
+		{
+			if ($object->{$key} == $id)
+			{
+				$object->to_delete = true;
+				return true;
+			}
+		}
+		return false;
+	}
+
+
+    /**
+     * Function to fetch children objects
+     */
+    public function fetchChild()
+    {
+		if($this->withChild && !empty($this->childtables) && !empty($this->fk_element))
+		{
+			foreach($this->childtables as &$childTable)
+			{
+                $className = ucfirst($childTable);
+
+                $this->{$className}=array();
+
+                $sql = 'SELECT rowid FROM '.MAIN_DB_PREFIX.$childTable.' WHERE '.$this->fk_element.' = '.$this->id;
+                $res = $this->db->query($sql);
+
+                if($res)
+                {
+                    while($obj = $this->db->fetch_object($res))
+                    {
+                        $o=new $className($this->db);
+                        $o->fetch($obj->rowid);
+
+                        $this->{$className}[] = $o;
+                    }
+                }
+                else
+                {
+                    $this->errors[] = $this->db->lasterror();
+                }
+			}
+		}
+	}
+
+    /**
+     * Function to update children data
+     *
+     * @param   User    $user   user object
+     */
+	public function saveChild(User &$user)
+    {
+		if($this->withChild && !empty($this->childtables) && !empty($this->fk_element))
+		{
+			foreach($this->childtables as &$childTable)
+			{
+				$className = ucfirst($childTable);
+				if(!empty($this->{$className}))
+				{
+					foreach($this->{$className} as $i => &$object)
+					{
+						$object->{$this->fk_element} = $this->id;
+						
+						$object->update($user);
+						if($this->unsetChildDeleted && isset($object->to_delete) && $object->to_delete==true) unset($this->{$className}[$i]);
+					}
+				}
+			}
+		}
+	}
+
+
+    /**
+     * Function to update object or create or delete if needed
+     *
+     * @param   User    $user   user object
+     * @return                  < 0 if ko, > 0 if ok
+     */
+    public function update(User &$user)
+    {
+		if (empty($this->id)) return $this->create($user); // To test, with that, no need to test on high level object, the core decide it, update just needed
+        elseif (isset($this->to_delete) && $this->to_delete==true) return $this->delete($user);
+
+        $error = 0;
+        $this->db->begin();
+
+        $res = $this->updateCommon($user);
+        if ($res)
+        {
+            $result = $this->call_trigger(strtoupper($this->element). '_UPDATE', $user);
+            if ($result < 0) $error++;
+            else $this->saveChild($user);
+        }
+        else
+        {
+            $error++;
+            $this->error = $this->db->lasterror();
+            $this->errors[] = $this->error;
+        }
+
+        if (empty($error))
+        {
+            $this->db->commit();
+            return $this->id;
+        }
+        else
+        {
+            $this->db->rollback();
+            return -1;
+        }
+
+	}
+
+    /**
+     * Function to create object in database
+     *
+     * @param   User    $user   user object
+     * @return                  < 0 if ko, > 0 if ok
+     */
+    public function create(User &$user)
+    {
+		if($this->id > 0) return $this->update($user);
+
+        $error = 0;
+        $this->db->begin();
+
+        $res = $this->createCommon($user);
+		if($res)
+		{
+			$this->id = $this->db->last_insert_id($this->table_element);
+
+			$result = $this->call_trigger(strtoupper($this->element). '_CREATE', $user);
+            if ($result < 0) $error++;
+            else $this->saveChild($user);
+		}
+		else
+        {
+            $error++;
+            $this->error = $this->db->lasterror();
+            $this->errors[] = $this->error;
+		}
+
+        if (empty($error))
+        {
+            $this->db->commit();
+            return $this->id;
+        }
+        else
+        {
+            $this->db->rollback();
+            return -1;
+        }
+	}
+
+    /**
+     * Function to delete object in database
+     *
+     * @param   User    $user   user object
+     * @return                  < 0 if ko, > 0 if ok
+     */
+	public function delete(User &$user)
+    {
+		if ($this->id <= 0) return 0;
+
+        $error = 0;
+        $this->db->begin();
+
+        $result = $this->call_trigger(strtoupper($this->element). '_DELETE', $user);
+        if ($result < 0) $error++;
+
+        if (!$error)
+        {
+            $this->deleteCommon($user);
+            if($this->withChild && !empty($this->childtables))
+            {
+                foreach($this->childtables as &$childTable)
+                {
+                    $className = ucfirst($childTable);
+                    if (!empty($this->{$className}))
+                    {
+                        foreach($this->{$className} as &$object)
+                        {
+                            $object->delete($user);
+                        }
+                    }
+                }
+            }
+        }
+
+        if (empty($error))
+        {
+            $this->db->commit();
+            return 1;
+        }
+        else
+        {
+            $this->error = $this->db->lasterror();
+            $this->errors[] = $this->error;
+            $this->db->rollback();
+            return -1;
+        }
+	}
+
+
+    /**
+     * Function to get a formatted date
+     *
+     * @param   string  $field  Attribute to return
+     * @param   string  $format Output date format
+     * @return          string
+     */
+    public function getDate($field, $format='')
+    {
+		if(empty($this->{$field})) return '';
+		else
+        {
+			return dol_print_date($this->{$field}, $format);
+		}
+	}
+
+    /**
+     * Function to set date in field
+     *
+     * @param   string  $field  field to set
+     * @param   string  $date   formatted date to convert
+     * @return                  mixed
+     */
+    public function setDate($field, $date)
+    {
+	  	if (empty($date))
+	  	{
+	  		$this->{$field} = 0;
+	  	}
+		else
+        {
+			require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
+			$this->{$field} = dol_stringtotime($date);
+		}
+
+		return $this->{$field};
+	}
+
+
+    /**
+     * Function to update current object
+     *
+     * @param   array   $Tab    Array of values
+     * @return                  int
+     */
+    public function setValues(&$Tab)
+    {
+		foreach ($Tab as $key => $value)
+		{
+			if($this->checkFieldType($key, 'date'))
+			{
+				$this->setDate($key, $value);
+			}
+			else if( $this->checkFieldType($key, 'array'))
+			{
+				$this->{$key} = $value;
+			}
+			else if( $this->checkFieldType($key, 'float') )
+			{
+				$this->{$key} = (double) price2num($value);
+			}
+			else if( $this->checkFieldType($key, 'int') ) {
+				$this->{$key} = (int) price2num($value);
+			}
+			else
+            {
+				$this->{$key} = $value;
+			}
+		}
+
+		return 1;
+	}
+
+}

+ 500 - 0
htdocs/core/class/ctyperesource.class.php

@@ -0,0 +1,500 @@
+<?php
+/* Copyright (C) 2007-2012  Laurent Destailleur <eldy@users.sourceforge.net>
+ * Copyright (C) 2014-2016  Juanjo Menent       <jmenent@2byte.es>
+ * Copyright (C) 2016       Florian Henry       <florian.henry@atm-consulting.fr>
+ * Copyright (C) 2015       Raphaël Doursenaud  <rdoursenaud@gpcsolutions.fr>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 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/>.
+ */
+
+/**
+ * \file    resource/ctyperesource.class.php
+ * \ingroup resource
+ */
+
+/**
+ * Class Ctyperesource
+ *
+ * Put here description of your class
+ *
+ * @see CommonObject
+ */
+class Ctyperesource
+{
+	/**
+	 * @var string Id to identify managed objects
+	 */
+	public $element = 'ctyperesource';
+	/**
+	 * @var string Name of table without prefix where object is stored
+	 */
+	public $table_element = 'c_type_resource';
+
+	/**
+	 * @var CtyperesourceLine[] Lines
+	 */
+	public $lines = array();
+
+	/**
+	 */
+	
+	public $code;
+	public $label;
+	public $active;
+
+	/**
+	 */
+	
+
+	/**
+	 * Constructor
+	 *
+	 * @param DoliDb $db Database handler
+	 */
+	public function __construct(DoliDB $db)
+	{
+		$this->db = $db;
+	}
+
+	/**
+	 * Create object into database
+	 *
+	 * @param  User $user      User that creates
+	 * @param  bool $notrigger false=launch triggers after, true=disable triggers
+	 *
+	 * @return int <0 if KO, Id of created object if OK
+	 */
+	public function create(User $user, $notrigger = false)
+	{
+		dol_syslog(__METHOD__, LOG_DEBUG);
+
+		$error = 0;
+
+		// Clean parameters
+		
+		if (isset($this->code)) {
+			 $this->code = trim($this->code);
+		}
+		if (isset($this->label)) {
+			 $this->label = trim($this->label);
+		}
+		if (isset($this->active)) {
+			 $this->active = trim($this->active);
+		}
+
+		
+
+		// Check parameters
+		// Put here code to add control on parameters values
+
+		// Insert request
+		$sql = 'INSERT INTO ' . MAIN_DB_PREFIX . $this->table_element . '(';
+		
+		$sql.= 'code,';
+		$sql.= 'label';
+		$sql.= 'active';
+
+		
+		$sql .= ') VALUES (';
+		
+		$sql .= ' '.(! isset($this->code)?'NULL':"'".$this->db->escape($this->code)."'").',';
+		$sql .= ' '.(! isset($this->label)?'NULL':"'".$this->db->escape($this->label)."'").',';
+		$sql .= ' '.(! isset($this->active)?'NULL':$this->active);
+
+		
+		$sql .= ')';
+
+		$this->db->begin();
+
+		$resql = $this->db->query($sql);
+		if (!$resql) {
+			$error ++;
+			$this->errors[] = 'Error ' . $this->db->lasterror();
+			dol_syslog(__METHOD__ . ' ' . implode(',', $this->errors), LOG_ERR);
+		}
+
+		if (!$error) {
+			$this->id = $this->db->last_insert_id(MAIN_DB_PREFIX . $this->table_element);
+
+			if (!$notrigger) {
+				// Uncomment this and change MYOBJECT to your own tag if you
+				// want this action to call a trigger.
+
+				//// Call triggers
+				//$result=$this->call_trigger('MYOBJECT_CREATE',$user);
+				//if ($result < 0) $error++;
+				//// End call triggers
+			}
+		}
+
+		// Commit or rollback
+		if ($error) {
+			$this->db->rollback();
+
+			return - 1 * $error;
+		} else {
+			$this->db->commit();
+
+			return $this->id;
+		}
+	}
+
+	/**
+	 * Load object in memory from the database
+	 *
+	 * @param int    $id  Id object
+	 * @param string $code code
+	 * @param string $label Label
+	 *
+	 * @return int <0 if KO, 0 if not found, >0 if OK
+	 */
+	public function fetch($id,$code='',$label='')
+	{
+		dol_syslog(__METHOD__, LOG_DEBUG);
+
+		$sql = 'SELECT';
+		$sql .= ' t.rowid,';
+		
+		$sql .= " t.code,";
+		$sql .= " t.label,";
+		$sql .= " t.active";
+
+		
+		$sql .= ' FROM ' . MAIN_DB_PREFIX . $this->table_element . ' as t';
+		if ($id)   $sql.= " WHERE t.id = ".$id;
+		elseif ($code) $sql.= " WHERE t.code = '".$this->db->escape($code)."'";
+		elseif ($label) $sql.= " WHERE t.label = '".$this->db->escape($label)."'";
+		
+		
+		$resql = $this->db->query($sql);
+		if ($resql) {
+			$numrows = $this->db->num_rows($resql);
+			if ($numrows) {
+				$obj = $this->db->fetch_object($resql);
+
+				$this->id = $obj->rowid;
+				
+				$this->code = $obj->code;
+				$this->label = $obj->label;
+				$this->active = $obj->active;
+
+				
+			}
+			
+			// Retrieve all extrafields for invoice
+			// fetch optionals attributes and labels
+			/*
+			require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
+			$extrafields=new ExtraFields($this->db);
+			$extralabels=$extrafields->fetch_name_optionals_label($this->table_element,true);
+			$this->fetch_optionals($this->id,$extralabels);
+            */
+			
+			// $this->fetch_lines();
+			
+			$this->db->free($resql);
+
+			if ($numrows) {
+				return 1;
+			} else {
+				return 0;
+			}
+		} else {
+			$this->errors[] = 'Error ' . $this->db->lasterror();
+			dol_syslog(__METHOD__ . ' ' . implode(',', $this->errors), LOG_ERR);
+
+			return - 1;
+		}
+	}
+
+	/**
+	 * Load object in memory from the database
+	 *
+	 * @param string $sortorder Sort Order
+	 * @param string $sortfield Sort field
+	 * @param int    $limit     offset limit
+	 * @param int    $offset    offset limit
+	 * @param array  $filter    filter array
+	 * @param string $filtermode filter mode (AND or OR)
+	 *
+	 * @return int <0 if KO, >0 if OK
+	 */
+	public function fetchAll($sortorder='', $sortfield='', $limit=0, $offset=0, array $filter = array(), $filtermode='AND')
+	{
+		dol_syslog(__METHOD__, LOG_DEBUG);
+
+		$sql = 'SELECT';
+		$sql .= ' t.rowid,';
+		
+		$sql .= " t.code,";
+		$sql .= " t.label,";
+		$sql .= " t.active";
+
+		
+		$sql .= ' FROM ' . MAIN_DB_PREFIX . $this->table_element. ' as t';
+
+		// Manage filter
+		$sqlwhere = array();
+		if (count($filter) > 0) {
+			foreach ($filter as $key => $value) {
+				$sqlwhere [] = $key . ' LIKE \'%' . $this->db->escape($value) . '%\'';
+			}
+		}
+		
+		if (count($sqlwhere) > 0) {
+			$sql .= ' WHERE ' . implode(' '.$filtermode.' ', $sqlwhere);
+		}
+		if (!empty($sortfield)) {
+			$sql .= $this->db->order($sortfield,$sortorder);
+		}
+		if (!empty($limit)) {
+		 $sql .=  ' ' . $this->db->plimit($limit, $offset);
+		}
+
+		$resql = $this->db->query($sql);
+		if ($resql) {
+			$num = $this->db->num_rows($resql);
+
+			while ($obj = $this->db->fetch_object($resql)) {
+				$line = new self($this->db);
+
+				$line->id = $obj->rowid;
+				
+				$line->code = $obj->code;
+				$line->label = $obj->label;
+				$line->active = $obj->active;
+
+				
+			}
+			$this->db->free($resql);
+
+			return $num;
+		} else {
+			$this->errors[] = 'Error ' . $this->db->lasterror();
+			dol_syslog(__METHOD__ . ' ' . implode(',', $this->errors), LOG_ERR);
+
+			return - 1;
+		}
+	}
+
+	/**
+	 * Update object into database
+	 *
+	 * @param  User $user      User that modifies
+	 * @param  bool $notrigger false=launch triggers after, true=disable triggers
+	 *
+	 * @return int <0 if KO, >0 if OK
+	 */
+	public function update(User $user, $notrigger = false)
+	{
+		$error = 0;
+
+		dol_syslog(__METHOD__, LOG_DEBUG);
+
+		// Clean parameters
+		
+		if (isset($this->code)) {
+			 $this->code = trim($this->code);
+		}
+		if (isset($this->label)) {
+			 $this->label = trim($this->label);
+		}
+		if (isset($this->active)) {
+			 $this->active = trim($this->active);
+		}
+
+		// Check parameters
+		// Put here code to add a control on parameters values
+
+		// Update request
+		$sql = 'UPDATE ' . MAIN_DB_PREFIX . $this->table_element . ' SET';
+		
+		$sql .= ' code = '.(isset($this->code)?"'".$this->db->escape($this->code)."'":"null").',';
+		$sql .= ' label = '.(isset($this->label)?"'".$this->db->escape($this->label)."'":"null").',';
+		$sql .= ' active = '.(isset($this->active)?$this->active:"null");
+
+        
+		$sql .= ' WHERE rowid=' . $this->id;
+
+		$this->db->begin();
+
+		$resql = $this->db->query($sql);
+		if (!$resql) {
+			$error ++;
+			$this->errors[] = 'Error ' . $this->db->lasterror();
+			dol_syslog(__METHOD__ . ' ' . implode(',', $this->errors), LOG_ERR);
+		}
+
+		if (!$error && !$notrigger) {
+			// Uncomment this and change MYOBJECT to your own tag if you
+			// want this action calls a trigger.
+
+			//// Call triggers
+			//$result=$this->call_trigger('MYOBJECT_MODIFY',$user);
+			//if ($result < 0) { $error++; //Do also what you must do to rollback action if trigger fail}
+			//// End call triggers
+		}
+
+		// Commit or rollback
+		if ($error) {
+			$this->db->rollback();
+
+			return - 1 * $error;
+		} else {
+			$this->db->commit();
+
+			return 1;
+		}
+	}
+
+	/**
+	 * Delete object in database
+	 *
+	 * @param User $user      User that deletes
+	 * @param bool $notrigger false=launch triggers after, true=disable triggers
+	 *
+	 * @return int <0 if KO, >0 if OK
+	 */
+	public function delete(User $user, $notrigger = false)
+	{
+		dol_syslog(__METHOD__, LOG_DEBUG);
+
+		$error = 0;
+
+		$this->db->begin();
+
+		if (!$error) {
+			if (!$notrigger) {
+				// Uncomment this and change MYOBJECT to your own tag if you
+				// want this action calls a trigger.
+
+				//// Call triggers
+				//$result=$this->call_trigger('MYOBJECT_DELETE',$user);
+				//if ($result < 0) { $error++; //Do also what you must do to rollback action if trigger fail}
+				//// End call triggers
+			}
+		}
+
+		// If you need to delete child tables to, you can insert them here
+		
+		if (!$error) {
+			$sql = 'DELETE FROM ' . MAIN_DB_PREFIX . $this->table_element;
+			$sql .= ' WHERE rowid=' . $this->id;
+
+			$resql = $this->db->query($sql);
+			if (!$resql) {
+				$error ++;
+				$this->errors[] = 'Error ' . $this->db->lasterror();
+				dol_syslog(__METHOD__ . ' ' . implode(',', $this->errors), LOG_ERR);
+			}
+		}
+
+		// Commit or rollback
+		if ($error) {
+			$this->db->rollback();
+
+			return - 1 * $error;
+		} else {
+			$this->db->commit();
+
+			return 1;
+		}
+	}
+
+	/**
+	 * Load an object from its id and create a new one in database
+	 *
+	 * @param int $fromid Id of object to clone
+	 *
+	 * @return int New id of clone
+	 */
+	public function createFromClone($fromid)
+	{
+		dol_syslog(__METHOD__, LOG_DEBUG);
+
+		global $user;
+		$error = 0;
+		$object = new Ctyperesource($this->db);
+
+		$this->db->begin();
+
+		// Load source object
+		$object->fetch($fromid);
+		// Reset object
+		$object->id = 0;
+
+		// Clear fields
+		// ...
+
+		// Create clone
+		$result = $object->create($user);
+
+		// Other options
+		if ($result < 0) {
+			$error ++;
+			$this->errors = $object->errors;
+			dol_syslog(__METHOD__ . ' ' . implode(',', $this->errors), LOG_ERR);
+		}
+
+		// End
+		if (!$error) {
+			$this->db->commit();
+
+			return $object->id;
+		} else {
+			$this->db->rollback();
+
+			return - 1;
+		}
+	}
+
+	/**
+	 * Initialise object with example values
+	 * Id must be 0 if object instance is a specimen
+	 *
+	 * @return void
+	 */
+	public function initAsSpecimen()
+	{
+		$this->id = 0;
+		
+		$this->code = '';
+		$this->label = '';
+		$this->active = '';
+	}
+
+}
+
+/**
+ * Class CtyperesourceLine
+ */
+class CtyperesourceLine
+{
+	/**
+	 * @var int ID
+	 */
+	public $id;
+	/**
+	 * @var mixed Sample line property 1
+	 */
+	
+	public $code;
+	public $label;
+	public $active;
+
+	/**
+	 * @var mixed Sample line property 2
+	 */
+	
+}

+ 4 - 0
htdocs/core/class/extrafields.class.php

@@ -727,6 +727,10 @@ class ExtraFields
     		{
     		    $showsize='minwidth400imp';
     		}
+    		elseif ($type == 'boolean')
+    		{
+    		    $showsize='';
+    		}
     		else
     		{
     			if (round($size) < 12)

+ 4 - 3
htdocs/core/class/html.form.class.php

@@ -492,10 +492,11 @@ class Form
      */
     function textwithpicto($text, $htmltext, $direction = 1, $type = 'help', $extracss = '', $noencodehtmltext = 0, $notabs = 2, $tooltiptrigger='')
     {
-        global $conf;
+        global $conf, $langs;
 
         $alt = '';
-
+        if ($tooltiptrigger) $alt=$langs->trans("ClickToShowHelp");
+        
         //For backwards compatibility
         if ($type == '0') $type = 'info';
         elseif ($type == '1') $type = 'help';
@@ -994,7 +995,7 @@ class Form
     				print img_picto($langs->trans("Search"), 'search');
     			}
     		}
-            print '<input type="text" class="minwidth100" name="search_'.$htmlname.'" id="search_'.$htmlname.'" value="'.$selected_input_value.'"'.$placeholder.' '.(!empty($conf->global->THIRDPARTY_SEARCH_AUTOFOCUS) ? 'autofocus' : '').' />';
+            print '<input type="text" class="'.$morecss.'" name="search_'.$htmlname.'" id="search_'.$htmlname.'" value="'.$selected_input_value.'"'.$placeholder.' '.(!empty($conf->global->THIRDPARTY_SEARCH_AUTOFOCUS) ? 'autofocus' : '').' />';
     		if ($hidelabel == 3) {
     			print img_picto($langs->trans("Search"), 'search');
     		}

+ 14 - 5
htdocs/core/class/html.formmail.class.php

@@ -1059,18 +1059,18 @@ class FormMail extends Form
 	}
 	
 	/**
-	 * Set substit array from object
+	 * Get list of substition keys available.
 	 * 
-	 * @param	string	$mode		'form', 'formwithlines', 'formforlines' or 'emailing'
+	 * @param	string	$mode		'formemail', 'formemailwithlines', 'formemailforlines', 'emailing', ...
 	 * @return	void
 	 */
-	function getAvailableSubstitKey($mode='form')
+	static function getAvailableSubstitKey($mode='formemail')
 	{
-		global $conf;
+		global $conf, $langs;
 		
 		$vars=array();
 		
-		if ($mode == 'form' || $mode == 'formwithlines' || $mode == 'formforlines')
+		if ($mode == 'formemail' || $mode == 'formemailwithlines' || $mode == 'formemailforlines')
 		{
 			$vars=array(
 				'__REF__', 
@@ -1133,6 +1133,15 @@ class FormMail extends Form
 				$vars['__SECUREKEYPAYPAL_MEMBER__']='';
 			}
 		}
+		
+		$tmparray=array();
+		$parameters=array('mode'=>$mode);
+		complete_substitutions_array($tmparray, $langs, null, $parameters);
+		foreach($tmparray as $key => $val)
+		{
+		    $vars[$key]=$key;
+		}
+		
 		return $vars;
 	}
 

+ 7 - 7
htdocs/core/class/html.formmailing.class.php

@@ -32,9 +32,9 @@ class FormMailing  extends Form
 	/**
 	 * Output a select with destinaries status
 	 * 
-	 * @param string $selectedid the selected id
-	 * @param string $htmlname name of controm
-	 * @param integer $show_empty show empty option
+	 * @param string   $selectedid     The selected id
+	 * @param string   $htmlname       Name of controm
+	 * @param integer  $show_empty     Show empty option
 	 * @return string HTML select
 	 */
 	public function selectDestinariesStatus($selectedid='',$htmlname='dest_status', $show_empty=0) {
@@ -46,13 +46,13 @@ class FormMailing  extends Form
 		$mailing = new Mailing($this->db);
 
 		$options = array();
-
+        
 		if ($show_empty) {
-			$options[''] = '';
+			$options[-2] = '';   // Note -1 is used for error
 		}
 
-		$options = array_merge($options, $mailing->statut_dest);
+        $options = $options + $mailing->statut_dest;
 
-		return Form::selectarray($htmlname, $options, $selectedid, 0, 0, 0, '', 1);
+        return Form::selectarray($htmlname, $options, $selectedid, 0, 0, 0, '', 1);
 	}
 }

+ 12 - 5
htdocs/core/db/DoliDB.class.php

@@ -220,9 +220,9 @@ abstract class DoliDB implements Database
 	/**
 	 * Define sort criteria of request
 	 *
-	 * @param	string	$sortfield  List of sort fields, separated by comma. Example: 't1.fielda, t2.fieldb'
+	 * @param	string	        $sortfield  List of sort fields, separated by comma. Example: 't1.fielda, t2.fieldb'
 	 * @param	'ASC'|'DESC'	$sortorder  Sort order
-	 * @return	string      		String to provide syntax of a sort sql string
+	 * @return	string      		        String to provide syntax of a sort sql string
 	 */
 	function order($sortfield=null,$sortorder=null)
 	{
@@ -230,18 +230,25 @@ abstract class DoliDB implements Database
 		{
 			$return='';
 			$fields=explode(',',$sortfield);
+			$orders=explode(',',$sortorder);
+			$i=0;
 			foreach($fields as $val)
 			{
 				if (! $return) $return.=' ORDER BY ';
-				else $return.=',';
+				else $return.=', ';
 
 				$return.=preg_replace('/[^0-9a-z_\.]/i','',$val);
+				
+				$tmpsortorder = trim($orders[$i]);
+				
 				// Only ASC and DESC values are valid SQL
-				if (strtoupper($sortorder) === 'ASC') {
+				if (strtoupper($tmpsortorder) === 'ASC') {
 					$return .= ' ASC';
-				} elseif (strtoupper($sortorder) === 'DESC') {
+				} elseif (strtoupper($tmpsortorder) === 'DESC') {
 					$return .= ' DESC';
 				}
+				
+				$i++;
 			}
 			return $return;
 		}

+ 7 - 5
htdocs/core/js/lib_notification.js.php

@@ -65,7 +65,7 @@ if (! ($_SERVER['HTTP_REFERER'] === $dolibarr_main_url_root . '/' || $_SERVER['H
    	// We set a delay before launching first test so next check will arrive after the time_auto_update compared to previous one.
     var time_first_execution = (time_auto_update - (nowtime - time_js_next_test)) * 1000;	//need milliseconds
     if (login != '') { 
-    	console.log("Launch browser notif check: setTimeout to wait time_first_execution="+time_first_execution+" before first check - nowtime = "+nowtime+" auto_check_events_not_before = "+auto_check_events_not_before+" time_js_next_test = "+time_js_next_test+" time_auto_update="+time_auto_update);
+    	console.log("Launch browser notif check: setTimeout is set to launch 'first_execution' function after a wait of time_first_execution="+time_first_execution+". nowtime (time php page generation) = "+nowtime+" auto_check_events_not_before (val in session)= "+auto_check_events_not_before+" time_js_next_test (max now,auto_check_events_not_before) = "+time_js_next_test+" time_auto_update="+time_auto_update);
     	setTimeout(first_execution, time_first_execution); 
     } //first run auto check
 
@@ -79,17 +79,18 @@ if (! ($_SERVER['HTTP_REFERER'] === $dolibarr_main_url_root . '/' || $_SERVER['H
     function check_events() {
     	if (Notification.permission === "granted")
     	{
-    		console.log("Call check_events time_js_next_test="+time_js_next_test);
-            $.ajax("<?php print dol_buildpath('/core/ajax/check_notifications.php', 1); ?>", {
-                type: "post",   // Usually post o get
+    		console.log("Call check_events time_js_next_test = date we are looking for event after ="+time_js_next_test);
+            $.ajax("<?php print DOL_URL_ROOT.'/core/ajax/check_notifications.php'; ?>", {
+                type: "post",   // Usually post or get
                 async: true,
                 data: {time: time_js_next_test},
                 success: function (result) {
                     var arr = JSON.parse(result);
                     if (arr.length > 0) {
+                    	var audio = null; 
                         <?php
                         if (! empty($conf->global->AGENDA_NOTIFICATION_SOUND)) {
-                            print 'var audio = new Audio(\''.DOL_URL_ROOT.'/theme/common/sound/notification_agenda.wav'.'\');';
+                            print 'audio = new Audio(\''.DOL_URL_ROOT.'/theme/common/sound/notification_agenda.wav'.'\');';
                         }
                         ?>
     
@@ -136,6 +137,7 @@ if (! ($_SERVER['HTTP_REFERER'] === $dolibarr_main_url_root . '/' || $_SERVER['H
         }
 
         time_js_next_test += time_auto_update;
+		console.log('Updated time_js_next_test. New value is '+time_js_next_test);
     }
 <?php 
 }

+ 166 - 0
htdocs/core/js/listview.js

@@ -0,0 +1,166 @@
+// Copyright (C) 2017 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 3 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/
+
+//
+// \file       htdocs/core/js/listview.js
+// \brief      File that include javascript functions for lists
+//
+var Listview_include = true;
+
+function Listview_OrderDown(idListe, column) {
+	var base_url = document.location.href;
+	
+	base_url = Listview_recup_form_param(idListe,base_url);
+	base_url = Listview_removeParam(base_url,'Listview['+encodeURIComponent(idListe)+'][orderBy]');
+	
+	base_url = Listview_removeParam(base_url,'get-all-for-export');
+	
+	document.location.href=Listview_modifyUrl(base_url,"Listview["+encodeURIComponent(idListe)+"][orderBy]["+encodeURIComponent(column)+"]","DESC");
+}
+function Listview_OrderUp(idListe, column) {
+	
+	var base_url = document.location.href;
+	
+	base_url = Listview_recup_form_param(idListe,base_url);
+	base_url = Listview_removeParam(base_url,'Listview['+encodeURIComponent(idListe)+'][orderBy]');
+	
+	base_url = Listview_removeParam(base_url,'get-all-for-export');
+	
+	document.location.href=Listview_modifyUrl(base_url,"Listview["+encodeURIComponent(idListe)+"][orderBy]["+encodeURIComponent(column)+"]","ASC");
+}
+function Listview_modifyUrl(strURL,paramName,paramNewValue){
+	    if (strURL.indexOf(paramName+'=')!=-1){
+        	
+                var strFirstPart=strURL.substring(0,strURL.indexOf(paramName+'=',0))+paramName+'=';
+                var strLastPart="";
+                if (strURL.indexOf('&',strFirstPart.length-1)>0)
+                      strLastPart=strURL.substring(strURL.indexOf('&',strFirstPart.length-1),strURL.length);
+              		  strURL=strFirstPart+paramNewValue+strLastPart;
+                }
+        else{
+                if (strURL.search('=')!=-1) // permet de verifier s'il y a dej� des param�tres dans l'URL
+                        strURL+='&'+paramName+'='+paramNewValue;
+                else
+                        strURL+='?'+paramName+'='+paramNewValue;
+        }
+        
+        return strURL;
+}
+function Listview_removeParam(strURL, paramMask) {
+	var cpt=0;
+	var url = '';
+	
+	 while(strURL.indexOf(paramMask)!=-1 && cpt++ <50){
+	 	var strFirstPart= strURL.substring(0,strURL.indexOf(paramMask)-1);
+	 	
+	 	var strLastPart='';
+	 	if (strURL.indexOf('&',strFirstPart.length+1)>0) {
+	 		strLastPart = strURL.substring(strURL.indexOf('&',strFirstPart.length+1),strURL.length);	
+	 	}
+	 		
+		url = strFirstPart+strLastPart;
+	 	
+	 }
+	 
+	 if(url=='')url = strURL;
+	 
+	 return url;
+}
+
+function Listview_recup_form_param(idListe,base_url) {
+	
+	$('#'+idListe+' tr.barre-recherche [listviewtbs],#'+idListe+' tr.barre-recherche-head input,#'+idListe+' tr.barre-recherche-head select,#'+idListe+' div.tabsAction input[listviewtbs]').each(function(i,item) {
+		if($(item).attr("name")) {
+			base_url = Listview_modifyUrl(base_url, $(item).attr("name") , $(item).val());
+		}
+		
+	});
+	
+	return base_url;
+}
+
+function Listview_GoToPage(idListe,pageNumber){
+	
+	var base_url = document.location.href;
+	
+	base_url = Listview_recup_form_param(idListe,base_url);
+	base_url =Listview_modifyUrl(base_url,"Listview["+encodeURIComponent(idListe)+"][page]",pageNumber);
+	
+	base_url = Listview_removeParam(base_url,'get-all-for-export');
+	
+	document.location.href=base_url;
+}
+function Listview_submitSearch(obj) {
+	
+	$form = $(obj).closest('form');
+	console.log($form);
+	if($form.length>0){
+		$form.submit();
+	}
+}
+function Listview_launch_downloadAs(mode,url,token,session_name) {
+	 $('#listviewdAS_export_form').remove();
+	
+	$form = $('<form action="'+url+'" method="post" name="listviewdAS_export_form" id="listTBSdAS_export_form"></form>');
+	$form.append('<input type="hidden" name="mode" value="'+mode+'" />');
+	$form.append('<input type="hidden" name="token" value="'+token+'" />');
+	$form.append('<input type="hidden" name="session_name" value="'+session_name+'" />');
+	
+	$('body').append($form);
+	
+    $('#listviewdAS_export_form').submit();
+	
+}
+
+function Listview_downloadAs(obj, mode,url,token,session_name) {
+	
+	$form = $(obj).closest('form');
+	$div = $form.find('div.tabsAction');
+	$div.append('<input type="hidden" listviewtbs="hidden" name="token" value="'+token+'" />');
+	$div.append('<input type="hidden" listviewtbs="hidden" name="mode" value="'+mode+'" />');
+	$div.append('<input type="hidden" listviewtbs="hidden" name="url" value="'+url+'" />');
+	$div.append('<input type="hidden" listviewtbs="hidden" name="session_name" value="'+session_name+'" />');
+	$div.append('<input type="hidden" listviewtbs="hidden" name="get-all-for-export" value="1" />');
+	
+	Listview_submitSearch(obj);
+}
+
+$(document).ready(function() {
+	$('tr.barre-recherche input').keypress(function(e) {
+	    if(e.which == 13) {
+	       
+	       var id_list = $(this).closest('table').attr('id');
+	       
+	       $('#'+id_list+' .list-search-link').click();
+	       
+	    }
+	});
+	
+	var $_GET = {};
+	
+	document.location.search.replace(/\??(?:([^=]+)=([^&]*)&?)/g, function () {
+	    function decode(s) {
+	        return decodeURIComponent(s.split("+").join(" "));
+	    }
+	
+	    $_GET[decode(arguments[1])] = decode(arguments[2]);
+	});
+	
+	if(typeof $_GET["get-all-for-export"] != "undefined") {
+		Listview_launch_downloadAs($_GET['mode'],$_GET['url'],$_GET['token'],$_GET['session_name']);
+	}
+	
+});

+ 92 - 0
htdocs/core/lib/accounting.lib.php

@@ -202,3 +202,95 @@ function length_accounta($accounta)
 		return $accounta;
 	}
 }
+
+
+
+/**
+ *	Show header of a VAT report
+ *
+ *	@param	string				$nom            Name of report
+ *	@param 	string				$variante       Link for alternate report
+ *	@param 	string				$period         Period of report
+ *	@param 	string				$periodlink     Link to switch period
+ *	@param 	string				$description    Description
+ *	@param 	timestamp|integer	$builddate      Date generation
+ *	@param 	string				$exportlink     Link for export or ''
+ *	@param	array				$moreparam		Array with list of params to add into form
+ *	@param	string				$calcmode		Calculation mode
+ *  @param  string              $varlink        Add a variable into the address of the page
+ *	@return	void
+ */
+function journalHead($nom,$variante,$period,$periodlink,$description,$builddate,$exportlink='',$moreparam=array(),$calcmode='', $varlink='')
+{
+    global $langs;
+
+    if (empty($hselected)) $hselected='report';
+
+    print "\n\n<!-- debut cartouche journal -->\n";
+
+    if(! empty($varlink)) $varlink = '?'.$varlink;
+
+    $h=0;
+    $head[$h][0] = $_SERVER["PHP_SELF"].$varlink;
+    $head[$h][1] = $langs->trans("Journalization");
+    $head[$h][2] = 'journal';
+
+    dol_fiche_head($head, 'journal');
+
+    print '<form method="POST" action="'.$_SERVER["PHP_SELF"].$varlink.'">';
+    foreach($moreparam as $key => $value)
+    {
+        print '<input type="hidden" name="'.$key.'" value="'.$value.'">';
+    }
+    print '<table width="100%" class="border">';
+
+    // Ligne de titre
+    print '<tr>';
+    print '<td width="110">'.$langs->trans("Name").'</td>';
+    if (! $variantexxx) print '<td colspan="3">';
+    else print '<td>';
+    print $nom;
+    if ($variantexxx) print '</td><td colspan="2">'.$variantexxx;
+    print '</td>';
+    print '</tr>';
+
+    // Calculation mode
+    if ($calcmode)
+    {
+        print '<tr>';
+        print '<td width="110">'.$langs->trans("CalculationMode").'</td>';
+        if (! $variante) print '<td colspan="3">';
+        else print '<td>';
+        print $calcmode;
+        if ($variante) print '</td><td colspan="2">'.$variante;
+        print '</td>';
+        print '</tr>';
+    }
+
+    // Ligne de la periode d'analyse du rapport
+    print '<tr>';
+    print '<td>'.$langs->trans("ReportPeriod").'</td>';
+    if (! $periodlink) print '<td colspan="3">';
+    else print '<td>';
+    if ($period) print $period;
+    if ($periodlink) print '</td><td colspan="2">'.$periodlink;
+    print '</td>';
+    print '</tr>';
+
+    // Ligne de description
+    print '<tr>';
+    print '<td>'.$langs->trans("ReportDescription").'</td>';
+    print '<td colspan="3">'.$description.'</td>';
+    print '</tr>';
+
+    print '</table>';
+
+    print '<br><div class="center"><input type="submit" class="button" name="submit" value="'.$langs->trans("Refresh").'"></div>';
+
+    print '</form>';
+
+    dol_fiche_end();
+
+    print "\n<!-- fin cartouche journal -->\n\n";
+}
+

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

@@ -349,7 +349,7 @@ function ajax_dialog($title,$message,$w=350,$h=150)
 
 
 /**
- * Make a input box content all selected
+ * Make content of an input box selected when we click into input field.
  * 
  * @param string	$htmlname	Id of html object 
  * @param int		$addlink	Add a link to after

+ 49 - 0
htdocs/core/lib/company.lib.php

@@ -496,6 +496,55 @@ function getFormeJuridiqueLabel($code)
     }
 }
 
+/**
+ *  Return if a country is inside the EEC (European Economic Community)
+ *  TODO Add a field into country dictionary.
+ *  
+ *  @param      Object      $object    Object
+ *  @return     boolean		           true = country inside EEC, false = country outside EEC
+ */
+function isInEEC($object)
+{
+    // List of all country codes that are in europe for european vat rules
+    // List found on http://ec.europa.eu/taxation_customs/common/faq/faq_1179_en.htm#9
+    $country_code_in_EEC=array(
+        'AT',	// Austria
+        'BE',	// Belgium
+        'BG',	// Bulgaria
+        'CY',	// Cyprus
+        'CZ',	// Czech republic
+        'DE',	// Germany
+        'DK',	// Danemark
+        'EE',	// Estonia
+        'ES',	// Spain
+        'FI',	// Finland
+        'FR',	// France
+        'GB',	// United Kingdom
+        'GR',	// Greece
+        'HR',   // Croatia
+        'NL',	// Holland
+        'HU',	// Hungary
+        'IE',	// Ireland
+        'IM',	// Isle of Man - Included in UK
+        'IT',	// Italy
+        'LT',	// Lithuania
+        'LU',	// Luxembourg
+        'LV',	// Latvia
+        'MC',	// Monaco - Included in France
+        'MT',	// Malta
+        //'NO',	// Norway
+        'PL',	// Poland
+        'PT',	// Portugal
+        'RO',	// Romania
+        'SE',	// Sweden
+        'SK',	// Slovakia
+        'SI',	// Slovenia
+        'UK',	// United Kingdom
+        //'CH',	// Switzerland - No. Swizerland in not in EEC
+    );
+    //print "dd".$this->country_code;
+    return in_array($object->country_code, $country_code_in_EEC);
+}
 
 
 /**

+ 24 - 6
htdocs/core/lib/files.lib.php

@@ -1495,10 +1495,10 @@ function dol_uncompress($inputfile,$outputdir)
 {
     global $conf, $langs;
 
-    if (defined('ODTPHP_PATHTOPCLZIP'))
+    if (! empty($conf->global->ODTPHP_PATHTOPCLZIP))
     {
-    	dol_syslog("Constant ODTPHP_PATHTOPCLZIP for pclzip library is set to ".constant('ODTPHP_PATHTOPCLZIP').", so we use Pclzip to unzip into ".$outputdir);
-        include_once ODTPHP_PATHTOPCLZIP.'/pclzip.lib.php';
+    	dol_syslog("Constant ODTPHP_PATHTOPCLZIP for pclzip library is set to ".$conf->global->ODTPHP_PATHTOPCLZIP.", so we use Pclzip to unzip into ".$outputdir);
+        include_once $conf->global->ODTPHP_PATHTOPCLZIP.'/pclzip.lib.php';
         $archive = new PclZip($inputfile);
         $result=$archive->extract(PCLZIP_OPT_PATH, $outputdir);
         //var_dump($result);
@@ -1584,7 +1584,7 @@ function dol_check_secure_access_document($modulepart,$original_file,$entity,$fu
 	$sqlprotectagainstexternals='';
 	$ret=array();
 
-	// find the subdirectory name as the reference
+    // Find the subdirectory name as the reference. For exemple original_file='10/myfile.pdf' -> refname='10'
 	if (empty($refname)) $refname=basename(dirname($original_file)."/");
 
 	$relative_original_file = $original_file;
@@ -1628,8 +1628,14 @@ function dol_check_secure_access_document($modulepart,$original_file,$entity,$fu
 	// Wrapping pour les apercu intervention
 	elseif (($modulepart == 'apercufichinter' || $modulepart == 'apercuficheinter') && !empty($conf->ficheinter->dir_output))
 	{
-		if ($fuser->rights->ficheinter->lire) $accessallowed=1;
-		$original_file=$conf->ficheinter->dir_output.'/'.$original_file;
+	    if ($fuser->rights->ficheinter->lire) $accessallowed=1;
+	    $original_file=$conf->ficheinter->dir_output.'/'.$original_file;
+	}
+	// Wrapping pour les apercu commande
+	elseif (($modulepart == 'apercusupplier_order' || $modulepart == 'apercusupplier_order') && !empty($conf->fournisseur->commande->dir_output))
+	{
+	    if ($fuser->rights->fournisseur->commande->lire) $accessallowed=1;
+	    $original_file=$conf->fournisseur->commande->dir_output.'/'.$original_file;
 	}
 	// Wrapping pour les images des stats propales
 	elseif ($modulepart == 'propalstats' && !empty($conf->propal->dir_temp))
@@ -1752,6 +1758,18 @@ function dol_check_secure_access_document($modulepart,$original_file,$entity,$fu
 		$original_file=$conf->fckeditor->dir_output.'/'.$original_file;
 	}
 
+	// Wrapping for users
+	else if ($modulepart == 'user' && !empty($conf->user->dir_output))
+	{
+        $canreaduser=(! empty($fuser->admin) || $fuser->rights->user->user->lire);
+        if ($fuser->id == (int) $refname) { $canreaduser=1; } // A user can always read its own card
+        if ($canreaduser || preg_match('/^specimen/i',$original_file))
+	    {
+	        $accessallowed=1;
+	    }
+	    $original_file=$conf->user->dir_output.'/'.$original_file;
+	}
+	
 	// Wrapping for third parties
 	else if (($modulepart == 'company' || $modulepart == 'societe') && !empty($conf->societe->dir_output))
 	{

+ 82 - 34
htdocs/core/lib/functions.lib.php

@@ -246,24 +246,64 @@ function dol_shutdown()
  *  @param  mixed   $options     Options to pass to filter_var when $check is set to custom
  *  @return string|string[]      Value found (string or array), or '' if check fails
  */
-function GETPOST($paramname,$check='',$method=0,$filter=NULL,$options=NULL)
+function GETPOST($paramname, $check='', $method=0, $filter=NULL, $options=NULL)
 {
 	if (empty($method))
 	{
 		$out = isset($_GET[$paramname])?$_GET[$paramname]:(isset($_POST[$paramname])?$_POST[$paramname]:'');
-		
+
 		// Management of default values
-		if (! empty($_GET['action']) && $_GET['action'] == 'create' && ! empty($paramname) && ! isset($_GET[$paramname]) && ! isset($_POST[$paramname]))
+		if (! isset($_GET['sortfield']))	// If we did a click on a field to sort, we do no apply default values
 		{
-			$relativepathstring = preg_replace('/\.[a-z]+$/', '', $_SERVER["PHP_SELF"]);
-			if (constant('DOL_URL_ROOT')) $relativepathstring = preg_replace('/^'.preg_quote(constant('DOL_URL_ROOT'),'/').'/', '', $relativepathstring);
-			$relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
-			$relativepathstring = preg_replace('/^\//', '', $relativepathstring);
-			$relativepathstring=dol_string_nospecial($relativepathstring, '-');
-			// $relativepathstring is now string that identify the page: '_societe_card', '_agenda_card', ...
-			$keyfordefaultvalue = 'MAIN_DEFAULT_FOR_'.$relativepathstring.'_'.$paramname;
-			global $conf;
-			if (isset($conf->global->$keyfordefaultvalue)) $out = $conf->global->$keyfordefaultvalue;
+		    if (! empty($_GET['action']) && $_GET['action'] == 'create' && ! empty($paramname) && ! isset($_GET[$paramname]) && ! isset($_POST[$paramname]))
+			{
+				$relativepathstring = $_SERVER["PHP_SELF"];
+				if (constant('DOL_URL_ROOT')) $relativepathstring = preg_replace('/^'.preg_quote(constant('DOL_URL_ROOT'),'/').'/', '', $relativepathstring);
+				$relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
+				$relativepathstring = preg_replace('/^\//', '', $relativepathstring);
+				global $user;
+				if (! empty($user->default_values))		// $user->default_values defined from menu default values, and values loaded not at first 
+				{
+					//var_dump($user->default_values[$relativepathstring]['createform']);
+					if (isset($user->default_values[$relativepathstring]['createform'][$paramname])) $out = $user->default_values[$relativepathstring]['createform'][$paramname];
+				}
+			}
+			// Management of default search_filters and sort order
+			elseif (preg_match('/list.php$/', $_SERVER["PHP_SELF"]) && ! empty($paramname) && ! isset($_GET[$paramname]) && ! isset($_POST[$paramname]))
+			{
+			    $relativepathstring = $_SERVER["PHP_SELF"];
+				if (constant('DOL_URL_ROOT')) $relativepathstring = preg_replace('/^'.preg_quote(constant('DOL_URL_ROOT'),'/').'/', '', $relativepathstring);
+				$relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
+				$relativepathstring = preg_replace('/^\//', '', $relativepathstring);
+				global $user;
+				if (! empty($user->default_values))		// $user->default_values defined from menu default values, and values loaded not at first 
+				{
+				    //var_dump($user->default_values[$relativepathstring]);
+        			if ($paramname == 'sortfield')
+        			{
+        			    if (isset($user->default_values[$relativepathstring]['sortorder'])) 
+        			    {
+        			        foreach($user->default_values[$relativepathstring]['sortorder'] as $key => $val)
+        			        {
+        			            if ($out) $out.=', ';
+        			            $out.=$key;
+        			        }
+        			    }
+        			}
+        			elseif ($paramname == 'sortorder')
+        			{
+        			    if (isset($user->default_values[$relativepathstring]['sortorder'])) 
+        			    {
+        			        foreach($user->default_values[$relativepathstring]['sortorder'] as $key => $val)
+        			        {
+        			            if ($out) $out.=', ';
+        			            $out.=$val;
+        			        }
+        			    }
+        			}
+				    elseif (isset($user->default_values[$relativepathstring]['filters'][$paramname])) $out = $user->default_values[$relativepathstring]['filters'][$paramname];
+				}
+			}
 		}
 	}
 	elseif ($method==1) $out = isset($_GET[$paramname])?$_GET[$paramname]:'';
@@ -303,6 +343,11 @@ function GETPOST($paramname,$check='',$method=0,$filter=NULL,$options=NULL)
 	            global $user;
 	            $out = $user->id;
 	        }
+	    	elseif ($reg[1] == 'SUPERVISORID')
+	        {
+	            global $user;
+	            $out = $user->fk_user;
+	        }
 	        elseif ($reg[1] == 'ENTITYID')
 	        {
 	            global $conf;
@@ -1020,11 +1065,11 @@ function dol_banner_tab($object, $paramid, $morehtml='', $shownav=1, $fieldid='r
 	if (! empty($conf->global->MAIN_USE_ADVANCED_PERMS) && empty($user->rights->barcode->lire_advance)) $showbarcode=0;
 	$modulepart='unknown';
 
-	if ($object->element == 'societe')   $modulepart='societe';
-	if ($object->element == 'contact')   $modulepart='contact';
-	if ($object->element == 'member')    $modulepart='memberphoto';
-	if ($object->element == 'user')      $modulepart='userphoto';
-	if ($object->element == 'product')   $modulepart='product';
+	if ($object->element == 'societe')         $modulepart='societe';
+	if ($object->element == 'contact')         $modulepart='contact';
+	if ($object->element == 'member')          $modulepart='memberphoto';
+	if ($object->element == 'user')            $modulepart='userphoto';
+	if ($object->element == 'product')         $modulepart='product';
 	if (class_exists("Imagick"))
 	{
 		if ($object->element == 'propal')    $modulepart='propal';
@@ -1032,6 +1077,7 @@ function dol_banner_tab($object, $paramid, $morehtml='', $shownav=1, $fieldid='r
 		if ($object->element == 'facture')   $modulepart='facture';
 		if ($object->element == 'fichinter') $modulepart='ficheinter';
 		if ($object->element == 'contrat')   $modulepart='contract';
+	    if ($object->element == 'order_supplier')  $modulepart='supplier_order';
 	}
 
 	if ($object->element == 'product')
@@ -1057,12 +1103,11 @@ function dol_banner_tab($object, $paramid, $morehtml='', $shownav=1, $fieldid='r
 	{
 		if ($showimage)
         {
-        	if ($modulepart != 'unknown')
+            if ($modulepart != 'unknown')
             {
                 $phototoshow='';
-                
                 // Check if a preview file is available
-                if (in_array($modulepart, array('propal', 'commande', 'facture', 'ficheinter', 'contract')) && class_exists("Imagick"))
+                if (in_array($modulepart, array('propal', 'commande', 'facture', 'ficheinter', 'contract', 'supplier_order')) && class_exists("Imagick"))
                 {
                     $objectref = dol_sanitizeFileName($object->ref);
                     $dir_output = $conf->$modulepart->dir_output . "/";
@@ -1092,7 +1137,7 @@ function dol_banner_tab($object, $paramid, $morehtml='', $shownav=1, $fieldid='r
                         if (file_exists($fileimage))
                         {
                             $phototoshow = '<div class="floatleft inline-block valignmiddle divphotoref"><div class="photoref">';
-                            $phototoshow.= '<img height="70" class="photo photowithmargin" src="'.DOL_URL_ROOT . '/viewimage.php?modulepart=apercu'.$modulepart.'&amp;file='.urlencode($relativepathimage).'">';
+                            $phototoshow.= '<img height="70" class="photo photowithmargin photowithborder" src="'.DOL_URL_ROOT . '/viewimage.php?modulepart=apercu'.$modulepart.'&amp;file='.urlencode($relativepathimage).'">';
                             $phototoshow.= '</div></div>';
                         }
                         // Si fichier png PDF de plus d'1 page trouve
@@ -1100,7 +1145,7 @@ function dol_banner_tab($object, $paramid, $morehtml='', $shownav=1, $fieldid='r
                         {
                             $preview = preg_replace('/\.png/','',$relativepathimage) . "-0.png";
                             $phototoshow = '<div class="floatleft inline-block valignmiddle divphotoref"><div class="photoref">';
-                            $phototoshow.= '<img height="70" class="photo photowithmargin" src="'.DOL_URL_ROOT . '/viewimage.php?modulepart=apercu'.$modulepart.'&amp;file='.urlencode($preview).'"><p>';
+                            $phototoshow.= '<img height="70" class="photo photowithmargin photowithborder" src="'.DOL_URL_ROOT . '/viewimage.php?modulepart=apercu'.$modulepart.'&amp;file='.urlencode($preview).'"><p>';
                             $phototoshow.= '</div></div>';
                         }
                     }
@@ -1126,7 +1171,6 @@ function dol_banner_tab($object, $paramid, $morehtml='', $shownav=1, $fieldid='r
                     $width=80;
                     $cssclass='photorefcenter';
                     $nophoto=img_picto('', 'title_agenda', '', false, 1);
-                    $morehtmlleft.='<div class="floatleft inline-block valignmiddle divphotoref"><div class="photoref"><img class="photo'.$modulepart.($cssclass?' '.$cssclass:'').'" alt="No photo" border="0"'.($width?' width="'.$width.'"':'').' src="'.$nophoto.'"></div></div>';
                 }
                 else
                 {
@@ -1134,8 +1178,8 @@ function dol_banner_tab($object, $paramid, $morehtml='', $shownav=1, $fieldid='r
                     $picto = $object->picto;
                     if ($object->element == 'project' && ! $object->public) $picto = 'project'; // instead of projectpub
     				$nophoto=img_picto('', 'object_'.$picto, '', false, 1);
-    				$morehtmlleft.='<div class="floatleft inline-block valignmiddle divphotoref"><div class="photoref"><img class="photo'.$modulepart.($cssclass?' '.$cssclass:'').'" alt="No photo" border="0"'.($width?' width="'.$width.'"':'').' src="'.$nophoto.'"></div></div>';
                 }
+                $morehtmlleft.='<!-- No photo to show --><div class="floatleft inline-block valignmiddle divphotoref"><div class="photoref"><img class="photo'.$modulepart.($cssclass?' '.$cssclass:'').'" alt="No photo" border="0"'.($width?' width="'.$width.'"':'').' src="'.$nophoto.'"></div></div>';
                 $morehtmlleft.='</div>';
             }
         }
@@ -3155,6 +3199,9 @@ function getTitleFieldOfList($name, $thead=0, $file="", $field="", $begin="", $m
 	$tag='th';
 	if ($thead==2) $tag='div';
 
+	$tmpsortfield=explode(',',$sortfield);
+	$sortfield=trim($tmpsortfield[0]);
+	
 	// If field is used as sort criteria we use a specific class
 	// Example if (sortfield,field)=("nom","xxx.nom") or (sortfield,field)=("nom","nom")
 	if ($field && ($sortfield == $field || $sortfield == preg_replace("/^[^\.]+\./","",$field))) $out.= '<'.$tag.' class="'.$prefix.'liste_titre_sel" '. $moreattrib.'>';
@@ -3169,13 +3216,13 @@ function getTitleFieldOfList($name, $thead=0, $file="", $field="", $begin="", $m
 
 		if ($field != $sortfield)
 		{
-            if ($sortorder == 'DESC') $out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">';
-            if ($sortorder == 'ASC' || ! $sortorder) $out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">';
+            if (preg_match('/^DESC/', $sortorder)) $out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">';
+            else $out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">';
 		}
 		else
 		{
-            if ($sortorder == 'DESC' || ! $sortorder) $out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">';
-            if ($sortorder == 'ASC') $out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">';
+            if (preg_match('/^ASC/', $sortorder)) $out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">';
+		    else $out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">';
 		}
 	}
 
@@ -3204,12 +3251,12 @@ function getTitleFieldOfList($name, $thead=0, $file="", $field="", $begin="", $m
 		}
 		else
 		{
-			if ($sortorder == 'DESC' ) {
+			if (preg_match('/^DESC/', $sortorder)) {
 				//$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">'.img_down("A-Z",0).'</a>';
 				//$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">'.img_up("Z-A",1).'</a>';
 				$sortimg.= '<span class="nowrap">'.img_up("Z-A",0).'</span>';
 			}
-			if ($sortorder == 'ASC' ) {
+			if (preg_match('/^ASC/', $sortorder)) {
 				//$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">'.img_down("A-Z",1).'</a>';
 				//$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">'.img_up("Z-A",0).'</a>';
 				$sortimg.= '<span class="nowrap">'.img_down("A-Z",0).'</span>';
@@ -3280,7 +3327,7 @@ function load_fiche_titre($titre, $morehtmlright='', $picto='title_generic.png',
 
 	$return.= "\n";
 	$return.= '<table '.($id?'id="'.$id.'" ':'').'summary="" class="centpercent notopnoleftnoright'.($morecssontable?' '.$morecssontable:'').'" style="margin-bottom: 2px;"><tr>';
-	if ($picto) $return.= '<td class="nobordernopadding widthpictotitle" valign="middle">'.img_picto('',$picto, 'id="pictotitle"', $pictoisfullpath).'</td>';
+	if ($picto) $return.= '<td class="nobordernopadding widthpictotitle" valign="middle">'.img_picto('',$picto, 'class="valignmiddle" id="pictotitle"', $pictoisfullpath).'</td>';
 	$return.= '<td class="nobordernopadding" valign="middle">';
 	$return.= '<div class="titre">'.$titre.'</div>';
 	$return.= '</td>';
@@ -3522,14 +3569,14 @@ function vatrate($rate, $addpercent=false, $info_bits=0, $usestarfornpr=0)
 	    $morelabel=' ('.$reg[1].')';
 	    $rate=preg_replace('/\s*'.preg_quote($morelabel,'/').'/','',$rate);
 	}
-	if (preg_match('/\*/',$rate) || preg_match('/'.constant('MAIN_LABEL_MENTION_NPR').'/i',$rate))
+	if (preg_match('/\*/',$rate))
 	{
 		$rate=str_replace('*','',$rate);
 		$info_bits |= 1;
 	}
 
 	$ret=price($rate,0,'',0,0).($addpercent?'%':'');
-	if ($info_bits & 1) $ret.=' '.($usestarfornpr?'*':constant('MAIN_LABEL_MENTION_NPR'));
+	if ($info_bits & 1) $ret.=' *';
 	$ret.=$morelabel;
 	return $ret;
 }
@@ -4905,7 +4952,8 @@ function make_substitutions($text, $substitutionarray, $outputlangs=null)
 }
 
 /**
- *  Complete the $substitutionarray with more entries
+ *  Complete the $substitutionarray with more entries.
+ *  Can also add substitution keys coming from external module that had set the "substitutions=1" into module_part array. In this case, method completesubstitutionarray provided by module is called. 
  *
  *  @param  array		$substitutionarray		Array substitution old value => new value value
  *  @param  Translate	$outputlangs            Output language

+ 2 - 1
htdocs/core/lib/functions2.lib.php

@@ -5,6 +5,7 @@
  * Copyright (C) 2014-2016  Marcos García               <marcosgdf@gmail.com>
  * Copyright (C) 2015       Ferran Marcet               <fmarcet@2byte.es>
  * Copyright (C) 2015-2016  Raphaël Doursenaud          <rdoursenaud@gpcsolutions.fr>
+ * Copyright (C) 2017       Juanjo Menent               <jmenent@2byte.es>
  *
  * 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
@@ -1439,7 +1440,7 @@ function dol_print_reduction($reduction,$langs)
     }
     else
     {
-        $string = $reduction.'%';
+        $string = price($reduction).'%';
     }
 
     return $string;

+ 28 - 5
htdocs/core/lib/pdf.lib.php

@@ -599,7 +599,14 @@ function pdf_getSubstitutionArray($outputlangs)
 	$substitutionarray=array(
 		'__MYCOMPANY_NAME__' => $mysoc->name,
 		'__MYCOMPANY_EMAIL__' => $mysoc->email,
-		'__USER_ID__' => $user->id,
+		'__MYCOMPANY_PROFID1__' => $mysoc->idprof1,
+		'__MYCOMPANY_PROFID2__' => $mysoc->idprof2,
+		'__MYCOMPANY_PROFID3__' => $mysoc->idprof3,
+		'__MYCOMPANY_PROFID4__' => $mysoc->idprof4,
+		'__MYCOMPANY_PROFID5__' => $mysoc->idprof5,
+		'__MYCOMPANY_PROFID6__' => $mysoc->idprof6,
+		'__MYCOMPANY_CAPITAL__' => $mysoc->capital,
+	    '__USER_ID__' => $user->id,
 		'__USER_LOGIN__' => $user->login,
 		'__USER_LASTNAME__' => $user->lastname,
 		'__USER_FIRSTNAME__' => $user->firstname,
@@ -1808,8 +1815,17 @@ function pdf_getlineprogress($object, $i, $outputlangs, $hidedetails = 0, $hookm
 	}
 	if (empty($reshook))
 	{
-        if ($object->lines[$i]->special_code == 3) return '';
-	    if (empty($hidedetails) || $hidedetails > 1) $result.=$object->lines[$i]->situation_percent . '%';
+        	if ($object->lines[$i]->special_code == 3) return '';
+		if (empty($hidedetails) || $hidedetails > 1)
+		{
+			if($conf->global->SITUATION_DISPLAY_DIFF_ON_PDF)
+			{
+			 	$prev_progress = $object->lines[$i]->get_prev_progress($object->id);
+			 	$result = ( $object->lines[$i]->situation_percent - $prev_progress) . '%';
+			}
+			else
+				$result = $object->lines[$i]->situation_percent . '%';
+	  	}
 	}
 	return $result;
 }
@@ -1844,7 +1860,7 @@ function pdf_getlinetotalexcltax($object,$i,$outputlangs,$hidedetails=0)
 		if(!empty($hookmanager->resPrint)) $result.=$hookmanager->resPrint;
 	}
     if (empty($reshook))
-	{
+    {
 	    if ($object->lines[$i]->special_code == 3)
     	{
     		return $outputlangs->transnoentities("Option");
@@ -1852,9 +1868,16 @@ function pdf_getlinetotalexcltax($object,$i,$outputlangs,$hidedetails=0)
         if (empty($hidedetails) || $hidedetails > 1)
         {
         	$total_ht = ($conf->multicurrency->enabled && $object->multicurrency_tx != 1 ? $object->lines[$i]->multicurrency_total_ht : $object->lines[$i]->total_ht);
+        	if ($object->lines[$i]->situation_percent > 0 )
+        	{
+		 	$prev_progress = $object->lines[$i]->get_prev_progress($object->id);
+		 	$progress = ( $object->lines[$i]->situation_percent - $prev_progress) /100;
+		 	$result.=price($sign * ($total_ht/($object->lines[$i]->situation_percent/100)) * $progress, 0, $outputlangs);
+		}
+        	else
 			$result.=price($sign * $total_ht, 0, $outputlangs);
-        }
 	}
+    }
 	return $result;
 }
 

+ 14 - 14
htdocs/core/lib/report.lib.php

@@ -24,20 +24,20 @@
 
 
 /**
-*	Show header of a VAT report
-*
-*	@param	string				$nom            Name of report
-*	@param 	string				$variante       Link for alternate report
-*	@param 	string				$period         Period of report
-*	@param 	string				$periodlink     Link to switch period
-*	@param 	string				$description    Description
-*	@param 	timestamp|integer	$builddate      Date generation
-*	@param 	string				$exportlink     Link for export or ''
-*	@param	array				$moreparam		Array with list of params to add into form
-*	@param	string				$calcmode		Calculation mode
-*   @param  string              $varlink        Add a variable into the address of the page
-*	@return	void
-*/
+ *	Show header of a VAT report
+ *
+ *	@param	string				$nom            Name of report
+ *	@param 	string				$variante       Link for alternate report
+ *	@param 	string				$period         Period of report
+ *	@param 	string				$periodlink     Link to switch period
+ *	@param 	string				$description    Description
+ *	@param 	timestamp|integer	$builddate      Date generation
+ *	@param 	string				$exportlink     Link for export or ''
+ *	@param	array				$moreparam		Array with list of params to add into form
+ *	@param	string				$calcmode		Calculation mode
+ *   @param  string              $varlink        Add a variable into the address of the page
+ *	@return	void
+ */
 function report_header($nom,$variante,$period,$periodlink,$description,$builddate,$exportlink='',$moreparam=array(),$calcmode='', $varlink='')
 {
 	global $langs;

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

@@ -150,9 +150,9 @@ function dol_loginfunction($langs,$conf,$mysoc)
 
 	// Title
 	$appli=constant('DOL_APPLICATION_TITLE');
-	$title=$appli.' '.DOL_VERSION;
+	$title=$appli.' '.constant('DOL_VERSION');
 	if (! empty($conf->global->MAIN_APPLICATION_TITLE)) $title=$conf->global->MAIN_APPLICATION_TITLE;
-	$titletruedolibarrversion=DOL_VERSION;	// $title used by login template after the @ to inform of true Dolibarr version
+	$titletruedolibarrversion=constant('DOL_VERSION');	// $title used by login template after the @ to inform of true Dolibarr version
 
 	// Note: $conf->css looks like '/theme/eldy/style.css.php'
 	$conf->css = "/theme/".(GETPOST('theme')?GETPOST('theme','alpha'):$conf->theme)."/style.css.php";

+ 15 - 3
htdocs/core/menus/standard/eldy.lib.php

@@ -545,7 +545,7 @@ function print_left_eldy_menu($db,$menu_array_before,$menu_array_after,&$tabMenu
 				$newmenu->add("/admin/ihm.php?mainmenu=home", $langs->trans("GUISetup"),1);
 
 				$newmenu->add("/admin/translation.php?mainmenu=home", $langs->trans("Translation"),1);
-				$newmenu->add("/admin/defaultvalues.php?mainmenu=home", $langs->trans("DefaultValues"),1, $conf->global->MAIN_FEATURES_LEVEL);
+				$newmenu->add("/admin/defaultvalues.php?mainmenu=home", $langs->trans("DefaultValues"),1);
 				$newmenu->add("/admin/boxes.php?mainmenu=home", $langs->trans("Boxes"),1);
 				$newmenu->add("/admin/delais.php?mainmenu=home",$langs->trans("MenuWarnings"),1);
 				$newmenu->add("/admin/security_other.php?mainmenu=home", $langs->trans("Security"),1);
@@ -800,9 +800,9 @@ function print_left_eldy_menu($db,$menu_array_before,$menu_array_after,&$tabMenu
 			if (! empty($conf->facture->enabled))
 			{
 				$langs->load("bills");
-				$newmenu->add("/compta/facture/list.php?leftmenu=customers_bills",$langs->trans("BillsCustomers"),0,$user->rights->facture->lire);
+				$newmenu->add("/compta/facture/list.php?leftmenu=customers_bills",$langs->trans("BillsCustomers"),0,$user->rights->facture->lire, '', $mainmenu, 'customers_bills');
 				$newmenu->add("/compta/facture/card.php?action=create",$langs->trans("NewBill"),1,$user->rights->facture->creer);
-				$newmenu->add("/compta/facture/list.php?leftmenu=customers_bills",$langs->trans("List"),1,$user->rights->facture->lire);
+				$newmenu->add("/compta/facture/list.php?leftmenu=customers_bills",$langs->trans("List"),1,$user->rights->facture->lire, '', $mainmenu, 'customers_bills');
 
 				if ($usemenuhider || empty($leftmenu) || preg_match('/customers_bills/', $leftmenu))
 				{
@@ -1252,6 +1252,18 @@ function print_left_eldy_menu($db,$menu_array_before,$menu_array_after,&$tabMenu
                 if ($conf->supplier_order->enabled) $newmenu->add("/product/stock/replenish.php", $langs->trans("Replenishment"), 1, $user->rights->stock->mouvement->creer && $user->rights->fournisseur->lire);
 			}
 
+			// Inventory
+			if ($conf->global->MAIN_LEVEL_FEATURES >= 2)
+			{
+    			if (! empty($conf->stock->enabled))
+    			{
+    				$langs->load("stocks");
+    				$newmenu->add("/product/inventory/list.php?leftmenu=stock", $langs->trans("Inventory"), 0, $user->rights->stock->lire, '', $mainmenu, 'stock');
+    				$newmenu->add("/product/inventory/card.php?action=create", $langs->trans("NewInventory"), 1, $user->rights->stock->creer);
+    				$newmenu->add("/product/inventory/list.php", $langs->trans("List"), 1, $user->rights->stock->lire);
+    			}
+			}
+			
 			// Expeditions
 			if (! empty($conf->expedition->enabled))
 			{

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